2008. 8. 24. 21:40

#define 보다는 const와 inline을 사용하라

#define ASPECT_RATIO 1.653

와 같이 전처리기 매크로를 사용하는 대신 상수를 정의한다.

const double ASPECT_RATIO = 1.653;

이 접근 방법에서 두 가지의 특별한 경우

1. 상수 포인터를 정의하기기 다소 까다로울 수 있다.

예로, 헤더 파일 내에서 const char * 기반의 스트링을 정의하기 위해선 const 를 두 번 이용해야 한다.

const char * const authorName = "Scott Meyers";

2. 클래스 범위의 상수를 정의하는 것이 편리할 때가 있다.

class GamePlayer {
private:
static const int NUM_TURNS=5; // 상수 선언
int scores[NUM_TURNS]; // 상수의 사용
....
};

하지만 위에서 보는 것은 NUM_TURNS을 위한 선언언(declaration)이지 정의(definition)가 아니라는 단점이 있다. 즉, 구현 파일에 정적 클래스 멤버를 정의해야 한다.

const int GamePlayer::NUM_TURNS; // 필수적인 정의, 클래스 구현 파일에 들어간다.

오래된 컴파일러들은 이러한 구문을 이해하지 못할 수 있다. (예로 Embeded Visual C++ 4.0 )
과거에는 선언의 시점에서 정적 클래스 멤버에 초기값을 할당하는 것이 불법이었다.

위 구문을 사용할 수 없을 경우에는 초기값을 정의 시점에 넣을 수 있다.

Class EngineeringConstans {
private:
static const double FUDGE_FACTOR;
...
};

// this goes in the class implementation file
const double EngineeringConstans::FUDGE_FACTOR = 1.35;

유일한 예외는 클래스의 컴파일시 클래스 상수의 값을 필요로할 때이다. 예를 들어, 앞에서 GamePlayer::scores의 선언에서 컴파일러는 컴파일 시기에 배열의 크기를 알아야 한다. 클래스 내 정수형 클래스 상수에 대해 초기값 지정을 금지하는 컴파일러는 보완하기 위한 일반적인 방법은 enum을 이용하는 것이다. ("enum hack"이라고 불림)

class GamePlayer {
private:
enum { NUM_TRUNS = 5 };

int scores[NUM_TURNS];
...
};

#define 디렉티브에 관한 또다른 하나의 요옹 예는 함수와 같은 매크로를 구현하기 위해서이다.

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

이와 같은 매크로를 작성할 때는 언제나 매크로 몸체의 모든 인자들을 괄호로 묶어야 한다는 것을 기억해야 한다. 그렇지 않으면 누군가 그 매크로를 호출하였을 때 문제가 발생할 수 있다.

하지만 이를 올바르게 적용했다고 할지라도 발생할 수 있는 신기한 상황을 살펴보자.

int a = 5, b = 0;
max(++a, b); // a가 두번 증가한다.
max(++a, b+10); // a가 한번 증가한다.

이와 같은 상황이 발생하지 않도록 하려면 inline 함수를 이용함으로써 매크로의 모든 효율성, 모든 예측 동작 및 정규 함수의 타입 안정성을 취할 수 있다.

inline int max(int a, int b) { return a > b ? a : b; }

이 버전의 max는 int로만 호출될 수 있기 때문에 위의 매크로와 사뭇 다르다. 하지만, 템플릿은 그 문제를 깨끗하게 해결한다.

template <class T>
inline const T& max(const T& a, const T& b)
{ return a > b ? a : b; }

그런데 max와 같은 일반적으로 이용되는 함수들을 위한 템플릿을 작성하는 것을 고려하기 전에 그들이 이미 존재하는지를 알아보기 위해 표준 라이브러리를 살펴 보아야 한다. max의 경우 표준 C++ 라이브러리에 포함되어 있다.

const 와 inline의 유용성으로 전처리기의 필요성이 감소하였지만 완전히 제거 되지는 않는다. 아직 컴파일러를 컨트롤하는 데 여전히 중요한 역할을 차지하고 있다. 아직 전처리기를 명퇴시킬 시기는 아니지만 더 길고 자주 휴가를 주기 시작할 것을 계획 해야 한다.

출처 : Effecitve C++, Scott Meyers 저