[C++] 항목 2: #define 을 쓰려거든 const, enum, inline을 떠올리자

업데이트:     Updated:

카테고리:

태그:

이 글은 아래의 책을 정리하였습니다.
이펙티브 C++ 제3판, 스콧 마이어스 저자, 곽용재 번역

📦 1. C++에 왔으면 C++의 법을 따릅시다

👉🏻 항목 2: #define 을 쓰려거든 const, enum, inline을 떠올리자

#define ASPECT_RATIO 1.635 // 매크로
const double AspectRatio = 1.635; // 상수

위 코드는 매크로, 아래 코드는 상수이다. 웬만해선 상수를 사용하는 것을 추천한다.

⚠️ 문제점

1. 유지 보수

소스 코드가 컴파일러에게 넘어가기 전에 선행 처리자가 밀어버리고 숫자 상수로 바꾸어버리기 때문에 유지 보수에 문제가 생길 수 있다.

이는 에러가 발생하게 될 때, ASPECT_RATIO가 아닌, 1.635로 나타나게 된다.

2. 사본 생성

상수는 여러번 쓰이더라도 사본이 하나지만, 매크로는 등장 횟수만큼 사본이 생긴다.

그러므로, 컴파일을 거친 최종 코드의 크기가 상수를 썼을 때보다 크게 나올 수 있다.

⚠️ 주의점

1. 상수 포인터를 정의하는 경우

const char * const authorName = "Scott Meyers";

‘포인터’와 ‘포인터가 가리키는 대상’까지 const를 선언하려 한다면, 위와 같이 두 번 써주어야 한다.

const std::string authorName("Scott Meyers");

현재는 ‘char *‘을 사용하는 것보다, string 객체를 사용하는 것을 권장한다는 것을 알아두자.

2. 클래스 상수를 정의하는 경우

class GamePlayer {
  private:
  static const int NumTurns = 5;
  int scores[NumTurns];
};

상수의 사본 개수가 한 개를 넘기지 못하도록 하려면 정적 멤버로 선언해주어야 한다.

그러나 위의 NumTurns는 선언된 것이지, 정의된 것이 아니다. 컴파일 타임에 상수로 올라간 것이 아니기 때문이다.

정의를 하기 위해선 아래와 같이 코드를 짜주어야 한다.

// 헤더 파일
class GamePlayer {
  private:
  static const int NumTurns;
  int scores[NumTurns];
};

// 구현 파일
const int GamePlayer::NumTurns = 5;

그러나 이렇게 짜면,

error: array bound is not an integer constant before ‘]’ token

이러한 에러가 생긴다. 이는 클래스 내에서 선언 이후 배열의 크기를 받아오려 했기에 생긴 오류다.

또 다시 이를 해결하기 위해선 아래와 같이 ‘나열자 둔갑술’을 사용하여야 한다.

class GamePlayer {
  private:
  enum { NumTurns = 5; }
  int scores[NumTurns];
}

마지막으로 클래스 상수를 #define하거나 캡슐화하는 방법은 없다.

3. 매크로 함수

매크로 함수에서도 #define 지시자를 오용하는 경우가 생긴다.

#define CALL_WITH_MAX(a, b) f((a)>(b) ? (a):(b))

int a=5, b=0;
# 1번째 : a가 두번 증가
CALL_WITH_MAX(++a, b);
# 2번째: a가 한번 증가
CALL_WITH_MAX(++a, b+10);

이러한 매크로는 큰 문제점을 안고 있다.

3.1. 1번째 경우
1번째에서 아래와 같은 형태로 들어가게 된다.

f((++a)>(b) ? (++a):(b))

bold 처리해준 부분들이 실행되어, a는 2번 증가한다.

3.2. 2번째 경우
2번째에서는 아래와 같은 형태로 들어간다.

f((++a)>(b+10) ? (++a):(b+10))

bold 처리해준 부분들이 실행되어, a는 1번 증가한다.

이러한 현상을 해결하기 위해선 인라인 함수 템플릿을 사용하면 된다.

template<typename T>
inline void callWithMax(const T& a, const T& b) {
  f(a>b ? a:b);
}

위와 같이 사용하면, 의도하지 않은 실수를 예방할 수 있다.

cpp 카테고리 내 다른 글 보러가기

댓글남기기