#include<iostream>
//#include<mutex>
// C++ 스킬22: 변수 정의는 늦출 수 있는 데까지 늦추자
// 변수를 정의하는 부분에서 생성자와 소멸자
//std::string encrypted; // 기본 생성자 호출
//encrypted = password; // 복사 대입 연산자 호출
//
//// 선언과 정의를 나눠서 하기보다는 한번에 하는 것이 좋다.
//std::string encrypted(password); //이런식으로 쓰는 것이 효율적
// C++ 스킬23: C 스타일의 캐스팅보다는 C++ 스타일의 캐스트를 사용하자.
// 1. const_cast : 상수성을 없애는 용도
// 2. dynamic_cast : 안전한 다운캐스팅을 할 떄 사용
// 3. reinterpret_cast : 포인터를 int로 바꿀때 사용
// 4. static_cast : 암시적 변환을 강제로 진행할 때 사용 ( double -> int, void* -> int*, 비상수 객체 -> 상수 객체 )
//
//class Widget {
//public:
// explicit Widget(int size);
//
//};
//
//void doSomeWork(const Widget& w);
//
//doSomeWork(Widget(15)); // 함수 방식 캐스트
//doSomeWork(static_cast<Widget>(15)); // static_cast 같은 결과
class Point {
public:
Point(int x, int y);
void setX(int newVal);
void setY(int newVal);
};
struct RectData {
Point ulhc;
Point lrhc;
};
class Rectangle {
public:
Point& upperLeft() const { return pData->ulhc; }
Point& lowerRight() const { return pData->lrhc; }
private:
std::tr1::shared_ptr<RectData> pData;
};
// C++ 스킬24: 예외 안정성을 확보하자
// 두가지 규칙
// 1. 자원이 새도록 만들지 않기
// 2. 자료구조가 더럽혀지는 것을 허용하지 않기
class PrettyMenu {
public:
void changeBackground(std::istream& imgSrc);
private:
Mutex mutex;
Image* bgImage;
int imageChanges;
};
void PrettyMenu::changeBackground(std::istream& imgSrc) {
lock(&mutex);
delete bgImage;
++imageChanges;
bgImage = new Image(imgSrc); // 사이에서 예외가 나오면 mutex가 잡힌 상태로 남아 자원이 샌다. // 자원관리 클래스를 사용
unlock(&mutex);
}
// copy and swap 방식으로 예외 안정성을 보장하는 법
// 객체를 수정할 때 객체의 사본을 만들어 놓고 사본을 수정하자
// 사본을 수정하는 과정에서 예외가 생겨도 원본 객체는 유지가 되기 때문에 좋다.
// 사본의 수정이 완료된 후에 수정된 객체를 원본 객체를 맞바꾸는 과정은 예외를 던지지 않는 연산 내부에서 수행하면 된다. // 강력한 예외 안정성 보장
// 진짜 객체의 모든 데이터를 별도의 구현 객체에 넣어두고
// 진짜 객체의 데이터 멤버로 구현객체를 가리키는 스마트포인터를 하나 만들어주면 좋다
struct PMImpl {
std::tr1::shared_ptr<Image> bgImage;
int imageChanges;
};
class PrettyMenu {
private:
Mutex mutex;
std::tr1::shared_ptr<PMImpl> pImpl;
};
void PrettyMenu::changeBackground(std::istream& imgSrc) {
using std::swap;
Lock m1(&mutex);
std::tr1::shared_ptr<PMImpl> pNew(new PMImpl(*pImpl)); //객체의 데이터 부분을 복사
pNew->bgImage.reset(new Image(imgSrc)); // 사본을 수정
++pNew->imageChanges;
swap(pImpl, pNew);
}
// C++ 스킬 25: inline 함수를 활용하자
// inline은 컴파일러에 요청을 하는 것이지 명령이 아니기 때문에 안 붙여도 자동으로 되는 경우도 있다.
// 암시적인 방법 : 클래스 정의 안에 함수를 바로 정의
// 명시적인 방법 : 함수 정의 앞에 inline 키워드를 붙이는 것
// 인라인 함수와 템플릿은 대개 헤더 파일 안에 정의한다.
// C++에서 함수 인라인은 컴파일 타임에 진행된다.
// 함수 인라인으 작고, 자주 호출되는 함수에 대해서만 하자
// C++ 스킬 26: 파일 사이의 컴파일 의존성을 최대로 줄이자
// C++ 클래스 정의는 클래스 인터페이스만 지정하는 것이 아니라 구현 세부사항 (데이터 멤버) 도 포함하고 있다.
// string Date 같은 타입의 데이터멤버를 포함한다면 이들을 위한 헤더파일도 포함 되어야 하므로 문제가 된다.
// 방법 1. 핸들 클래스 사용
// 방법 2. 인터페이스 클래스 사용
#include<string> // 표준 라이브러리 구성요소는 전방 선언을 할 수 없다. using namespace std{ class string }; 이런식으로 쓸 수 없다.
#include<memory>
class PersonImpl;
class Date; // 전방 선언을 통해 정의부에 대한 의존성을 선언부에 대한 의존성으로 바꾸기 #include "date.h"를 안써도 된다.
class Address;
// Person 클래스를 두개로 쪼개서 하나는 PersonImpl로
// ----------Person.h-------------//
class Person { // pimpl 관용구를 사용하는 클래스 (핸들 클래스)라고 부름
public:
Person(const std::string& name, const Date& birthday, const Address& addr);
std::string name() const;
std::string birthDate() const;
std::string address() const;
private:
std::tr1::shared_ptr<PersonImpl> pImpl;
};
// ----------PersonImpl.h-------------//
#include <string>
#include "date.h"
#include "address.h"
class PersonImpl { // person 구현 클래스
public:
PersonImpl(const std::string& name, const Date& birthday, const Address& addr);
std::string name() const { return theName; }
std::string birthDate() const { return theBirthDate; }
std::string address() const { return theAddress; }
private:
std::string theName;
Date theBirthDate;
Address theAddress;
};
// ------------- (Person 클래스의 구현) 핸들 클래스에 대응되는 구현 클래스(PersonImpl) 쪽으로 함수 호출을 전달하는 예제 -------------- //
#include "Person.h"
#include "PersonImpl.h"
Person::Person(const std::string& name, const Date& birthday, const Address& addr)
:pImpl(new PersonImpl(name, birthday, addr)) {}
std::string Person::name() const {
return pImpl->name();
}
// 인터페이스 클래스
class Person {
public:
virtual ~Person();
virtual std::string name() const = 0; // 순수 가상함수 가 있으면 자체적으로 인스턴스 만들 수 없으니 팩토리 함수가 필요
virtual std::string birthDate() const = 0;
virtual std::string address() const = 0;
static std::tr1::shared_ptr<Person> create(const std::string& name, const Date& birthday, const Address& addr); // 팩토리 함수
};
class RealPerson : public Person { // 인터페이스 구현
// 가상함수 전부 구현, 팩토리 함수 구현 해줘야 함
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
};

C++ 스킬 22: 변수 정의는 늦출 수 있는 데까지 늦추자

 

C++ 스킬 23: C 스타일의 캐스팅보다는 C++ 스타일의 캐스트를 사용하자.

캐스팅은 적게 사용할수록 좋다.

특히 dynamic_cast는 비용이 크므로 성능이 민감한 코드에서는 호출을 피하자.

 

C++ 스킬 24: 예외 안정성을 확보하자

ㄴ 자원 관리 클래스 사용하기 (스마트 포인터)

ㄴ Copy and swap 방식으로 객체를 수정하기 (swap은 예외를 던지지 않는 연산 내부에서 수행)

 

C++ 스킬 25: inline 함수를 활용하자

ㄴ 인라인 함수는 함수 호출 비용을 면제해준다.

ㄴ 인라인 함수는 대체로 헤더 파일에 들어 있게 만들자. 대부분의 빌드 환경에서 인라인을 컴파일 도중에 수행하기 때문에

ㄴ inline을 붙여도 inline이 될지 안될지는 컴파일러가 결정한다. 반복문이 있거나 재귀함수이거나 가상 함수 등은 인라인화 해주지 않는다.

 

C++ 스킬 26: 파일 사이의 컴파일 의존성을 최대로 줄이자

ㄴ 정의 대신에 선언에 의존하게 만들자

ㄴ 1. 핸들 클래스 사용하기

ㄴ 2. 인터페이스 클래스 사용하기

'읽은 책 > Effective C++' 카테고리의 다른 글

11. 템플릿  (0) 2022.04.27
10. 상속  (0) 2022.04.27
8. 소프트웨어 설계  (0) 2022.04.27
7. 자원관리 클래스와 스마트 포인터  (0) 2022.04.27
6. 대입 연산자  (0) 2022.04.27

+ Recent posts