티스토리 뷰

 

Effective C++ 이펙티브 C++
스콧 마이어스 저/곽용재
 

5. 구현

 

항목 26 : 변수 정의는 늦출 수 있는 데까지 늦추는 근성을  발휘하자

  • 변수 정의는 늦출 수 있을 때까지 늦춥시다. 프로그램이 더 깔끔해지며 효율도 좋아집니다.

 

항목 27 : 캐스팅은 절약, 또 절약! 잊지 말자

  • 다른 방법이 가능하다면 캐스팅은 피하십시오. 특히 수행 성능에 민감한 코드에서 dynamic_cast는 몇 번이고 다시 생각하십시오. 설계 중에 캐스팅이 필요해졌다면, 캐스팅을 쓰지 않는 다른 방법을 시도해 보십시오.
  • 캐스팅이 어쩔 수 없이 필요하다면, 함수 안에 숨길 수 있도록 해 보십시오. 이렇게 하면 최소한 사용자는 자신의 코드에 캐스팅을 넣지 않고 이 함수를 호출할 수 있게 됩니다.
  • 구형 스타일의 캐스트를 쓰려거든 C++ 스타일의 캐스트를 선호하십시오. 발견하기도 쉽고, 설계자가 어떤 역할을 의도했는지가 더 자세하게 드러납니다.

 

C++ 스타일의 캐스트

const_cast<T> (표현식)

dynamic_cast<T> (표현식)

reinterpret_cast<T> (표현식)

static_cast<T> (표현식)

참고 : [C++] type casting - static_cast, dynamic_cast, reinterpret_cast, const_cast

 

항목 28 : 내부에서 사용하는 객체에 대한 '핸들'을 반환하는 코드는 되도록 피하자

  • 어떤 객체의 내부요소에 대한 핸들(참조자, 포인터, 반복자)을 반환하는 것은 되도록 피하세요. 캡슐화 정도를 높이고, 상수 멤버 함수가 객체의 상수성을 유지한 채로 동작할 수 있도록 하며, 무효참조 핸들(dangling handle)이 생기는 경우를 최소화할 수 있습니다.

 

무효참조 핸들이 생기는 예

class GUIObject { ... };

// 어떤 GUI 객체의 사각 테두리 영역을 Rectangle 객체롤 반환하는 함수

const Rectangle boundingBox(const GUIObject& obj);

 

GUIObject *pgo;

...

// pgo가 가리키는 GUIObject의 사각 테두리 영역으로부터

// 좌측 상단 꼭짓점의 포인터를 얻습니다.

// boundingBox 함수의 반환 값이 소멸되니,

// 그 안에 들어 있는 Point 객체들도 덩달아 없어질 것입니다.

const Point *pUpperLeft = &(boundingBox(*pgo).upperLeft());

 

항목 29 : 예외 안전성이 확보되는 그날 위해 싸우고 또 싸우자!

예외 안정성을 갖춘 함수는 아래의 세 가지 보장 중 하나를 제공합니다.

기본적인 보장(basic guarantee)

함수 동작 중에 예외가 발생하면, 실행 중인 프로그램에 관련된 모든 것을 유효한 상태로 유지하겠다는 보장.

강력한 보장(strong guarantee)

함수 동작 중에 예외가 발생하면, 프로그램의 상태를 절대로 변경하지 않겠다는 보장.

예외불가 보장(nothrow guarantee)

예외를 절대로 던지지 않겠다는 보장.

예) int, 포인터 등 기본제공 타입

 

잘못된 예 - 배경 그림을 깔고 나오는 GUI 메뉴를 구현하기 위한 클래스

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;

++imageChange;

bgImage = new Image(imgSrc);

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 ml(&mutex);

// 객체의 데이터 부분을 복사합니다.

std::tr1::shared_ptr<PMImpl> pNew(new PMImpl(*pImpl));

// 사본을 수정합니다.

pNew->bgImage.reset(new Image(imgSrc));

++pNew->imageChanges;

// 새 데이터로 바꿔 넣어 진짜로 배경그림을 바꿉니다.

swap(pImpl, pNew);

}

 

  • 예외 안전성을 갖춘 함수는 실행 중 예외가 발생되더라도 자원을 누출시키지 않으며 자료구조를 더럽힌 채로 내버려 두지 않습니다. 이런 함수들이 제공할 수 있는 예외 안전성 보장은 기본적인 보장, 강력한 보장, 예외 금지 보장이 있습니다.
  • 강력한 예외 안전성 보장은 '복사-후-맞바꾸기' 방법을 써서 구현할 수 있지만, 모든 함수에 대해 강력한 보장이 실용적인 것은 아닙니다.
  • 어떤 함수가 제공하는 예외 안전성 보장의 강도는, 그 함수가 내부적으로 호출하는 함수들이 제공하는 가장 약한 보장을 넘지 않습니다.

 

- James Song

댓글