선행 처리자보다 컴파일러를 더 가까이 하자
#define ASPECT_RATIO 1.653
#define
을 사용하면 소스 코드가 컴파일러로 넘어가기 전에 선행 처리자가 기호식 이름을 숫자 상수로 변경함 (사용한 만큼 사본 생성)
-> ASPECT_RATIO
는 컴파일러가 쓰는 기호 테이블에 들어가지 않기 때문에 디버깅이 어려워짐
이런 문제는 상수를 통해 해결할 수 있음
const double AspectRatio = 1.653;
상수를 사용하면 컴파일러의 기호 테이블에 들어감
상수 타입의 ASPECT_RATIO
는 아무리 여러 번 쓰여도 사본은 딱 한 개만 생성됨
상수로 교체할 때 두 가지 경우를 조심해야 함
(1) 상수 포인터를 정의하는 경우
// 이 경우에는 char* 기반 문자열보다는 string을 사용하는 것이 좋음
const char* const myName = "Hello";
const std::string myName = "Hello";
(2) 클래스 멤버로 상수를 정의하는 경우
// .h 파일
class GamePlayer
{
private:
static const int NumTurns; // 상수 선언
int scores[NumTurns]; // 상수를 사용하는 부분
};
// .cpp 파일
const int GamePlayer::NumTurns = 5; // 상수 정의
정수 상수를 가지고 다른 사람이 주소를 얻는다던지 참조자를 쓴다던지 하는 경우가 싫다면 enum을 사용하는 것이 좋음
class GamePlayer
{
private:
enum { NumTurns = 5 };
int score[NumTurns];
};
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))
void f(int maxValue)
{
cout << "Max value: " << maxValue << endl;
}
int main()
{
int a = 5;
int b = 0;
CALL_WITH_MAX(++a, b); // a가 두 번 증가
CALL_WITH_MAX(++a, b + 10); // a가 한 번 증가
}
f()가 호출되기 전에 a가 증가하는 횟수가 달라짐
이 방법 보다는 템플릿을 사용해서 함수의 동작 방식과 타입 안정성을 모두 취하는 것이 좋음
template<typename T>
inline void callWithMax(const T& a, const T& b)
{
f(a > b ? a : b);
}
동일한 타입의 객체 두 개를 인자로 받고 둘 중 큰 것을 f에 넘겨서 호출하는 구조임
함수 본문에 괄호를 할 필요도 없고, 인자를 여러 번 평가해도 값이 변할 걱정이 없음
class GamePlayer
{
private:
static const int NumTurns = 5; // 선언
};
const int GamePlayer::NumTurns; // 정의
왜 static const int NumTurns = 5;
는 선언일까?
클래스 내부에서 정의된 것처럼 보이지만 컴파일러에게 실제 메모리 할당을 알려준 것은 아니기 때문에 선언임
class GamePlayer
{
private:
static const int NumTurns;
};
int main()
{
cout << GamePlayer::NumTurns << '\n';
}