본문 바로가기

Effective C++ 정리

02. 전처리기 보다 컴파일러와 가까이하기

#define 보다 cosnt, enum, inline

#define ASPECT_RATIO 1.653

 

위와 같은 ASPECT_RATIO 매크로 상수는 컴파일러가 쓰는 테이블에 포함되지 않습니다.

 

빌드하면 컴파일 이전에 전처리기가 매크로를 상수로 전부 바꿔버리죠.

그래서 컴파일러는 ASPECT_RATIO를 알 수 없게 됩니다.

 

// #define ASPECT_RATIO 1.653 // (X)
const double AspectRatio = 1.653; // (O)

 

이제 AspectRatio가 컴파일러 테이블에 포함되었습니다.

 

그리고 #define ASPECT_RATIO를 쓰지 않는 곳까지 치환되는 것을 막을 수 있어서

이전보다 파일의 크기도 작아질 수 있게 되었습니다.

 

const char* const CharacterName = "zozo";

 

책에서 문자열 포인터는 *(에스크리터) 앞 뒤로 const를 붙이라고 하는군요 !

 

static const 멤버 변수의 활용

클래스의 static const 멤버는 클래스 내부에 사본으로 돌려 쓸 때 좋다고 합니다.

 

// GameRule.h //

class GameRule
{
  private:
      static const int TurnCounts = 10;
      int Scores[TurnCounts]; // 상수를 사용하려는 부분
};

 

그러나 구형 컴파일러는 위와 같은 방법이 받아들여지지 않을때가 있습니다.

 

// GameRule.cpp //

const int GameRule::MaxTurn;

 

이럴 땐 위와 같이 cpp에 정적 변수를 초기화합니다. 

 

// GameRule.h //

class GameRule
{
  private:
      static const int TurnCounts;
      int Scores[TurnCounts];
};

 

// GameRule.cpp //

const int CGameRule::MaxTurn = 10;

 

그런데 가~끔 여타 컴파일러에서 TurnCounts 배열의 크기를 미리 알고 싶어서 

컴파일 에러가 나올 수 있다고 합니다.

 

여기서 #define TURNCOUNTS 10과 같은 매크로 상수로 대체했을 경우,

#undef로 유효 범위를 따로 설정하지 않는 한 캡슐화의 혜택을 받을 수 없습니다.

이럴 땐 오히려 클래스 내부에서 enum 타입을 활용하는 것이 안전합니다.

 

enumerator 둔갑술

enumerator 타입은 int가 놓인 곳에 쓰일 수 있다는 점을 활용합니다.

 

// GameRule.h //

class GameRule
{
  private:
      enum
      {
         TurnCounts = 10
      };

      int Scores[TurnCounts]; // 배열의 크기에 대한 컴파일 에러가 사라진다.
};

 

배열의 크기를 알고싶어하는 컴파일 에러가 사라졌습니다.

위에 있는 TurnCounts는 #define과 비슷하게 동작하다고 합니다.

TurnCounts를 위한 별도의 메모리를 할당하지 않아서 그런 것 같습니다.

실제로 이 방법은 템플릿 메타 프로그래밍에서 핵심 기법이라는군요.

 

#define 매크로 함수 대신 인라인 템플릿 함수 사용하기

함수의 호출 과정을 없애고 싶어서 아래와 같은 매크로 함수를 정의했습니다.

 

#define MAX_NUMBER(a,b)    ((a) > (b) ? (a) : (b))

 

int a = 5, b = 0;
MAX_NUMBER(++a, b) // a가 두 번 증가함
MAX_NUMbER(++a, b = 999) // a가 한 번 증가함

 

그러나 전처리기의 단순 치환 ++ 한 번, 비교 과정 ++ 한 번으로

a가 두 번 증가해서 의도치않은 결과가 나올 수 있습니다.

 

이럴 땐 인라인 템플릿 함수를 활용할 수 있습니다.

진짜 함수이기 때문에 인자의 유효 범위와 규칙을 그대로 따라갑니다.

 

template <typename T>
inline int MaxNumber(const int& a, const int& b)
{
   return (a > b ? a : b);
}