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

Minsu._.Lighting·2023년 11월 12일
0

[ Effective C++ ] 정리 모음집
" C++ 프로그래머의 필독서, 스콧 마이어스의 Effective C++ 를 읽고 내용 요약 / 정리 "

[핵심]

" 가급적 선행처리자보다 컴파일러를 더 생각하자 "

  • 단순한 상수를 쓸 때는, #define보다 const 객체 혹은 enum을 우선 생각하자!
  • 함수처럼 쓰이는 매크로를 만들려면, #define 매크로보다 인라인 함수를 우선 생각하자!

💡 매크로 대신 상수를 사용하자!

📌 상수를 #define 매크로를 이용해 사용하면???

[ 매크로 정의 ]
#define ASPECT_RATIO 1.653

  • 컴파일러가 사용하는 기호 테이블에 등록되지 않음.
    코드가 컴파일러에게 넘어가기 전 선행 처리자가 상수로 바꿔버리기 때문

  • 컴파일 에러, 디버깅 시 기호가 아닌 상수로 표현되어 헷갈릴 수 있음.
    매크로를 하나하나 다 기억할 수도 없을 뿐더러 남의 코드라면 더더욱!



📌 매크로 대신 상수를 사용하면???

[ 상수 선언 ]
const double AspectRatio = 1.653;

  • 컴파일러가 사용하는 기호 테이블에 등록.
    에러, 디버깅시 보다 쉽고 빠르게 문제 해결 가능!

  • 부동소수점 실수 타입일 경우 컴파일을 거친 최종코드의 크기가 보다 작게 나올 수 있음.
    매크로 사용 시 코드에 매크로가 등장할 때마다 선행처리자에 의해 상수로 바뀌어 등장 횟수만큼 사본이 만들어짐, 상수 사용 시 사본은 딱 한번 생김!

  • 상수 데이터 멤버는 매크로와 다르게 캡슐화가 가능!


📌 상수 사용 시 주의할 점!!!

  • 상수 포인터를 정의하는 경우
    다른 소스 파일이 include해 사용하기 위해 상수 정의는 대개 헤더파일에 넣는것이 상례,
    때문에 포인터는 꼭 const로 선언, 포인터가 가리키는 대상까지 const로 선언해 줌.
[ 상수 포인터 선언 ]
const char* const szName = "Minsu Lighting";
  • 클래스 멤버로 상수를 정의하는 경우
    상수의 유효범위를 클래스로 한정할 때, 그 상수의 사본 개수가 한 개를 넘지 못하게 하고 싶으면
    정적(static) 멤버로 만들어 줌.
[ 클래스 멤버 상수 선언 ]
class Player
{
private:
	static const int NumTurns = 5;			// 상수 선언
    int scores[NumTurns];					// 상수 사용 부분
}

📢 위 코드를 보면 상수의 '선언'만 있고 '정의'가 없는걸 확인할 수 있는데...
C++에서는 사용하고자 하는 것에 대한 '정의'가 마련되어 있어야 하지만, 정적 멤버로 만들어지는 정수류(각종 정수 타입, char, bool 등) 타입의 클래스 내부 상수는 예외!


📢 하지만 별도의 정의를 제공해야 하는 경우도 있다!

  • 클래스 상수의 주소가 필요한 경우!
  • 컴파일러가 잘 못 만들어진 관계로 정의를 요구하는 경우
[ 클래스 멤버 상수 정의 ]
const int Player::NumTurns;

- 정의는 헤더 파일이 아닌 구현 파일에 둠.
- 클래스 상수의 초기 값은 선언된 시점에 주어졌기 때문에 정의에는 값을 주지 않음.


📢 일부 컴파일러의 경우 위의 문법을 사용하지 못 함
컴파일러가 선언 시점에 초기 값을 주는 것이 맞지 않다고 판단하기 때문인데...
이 때에는 정의 시점에 초기 값을 주면 된다!

class CostEstimate
{
private:
	static const double FudgeFactor;
    ...
};
.
.
.
const double CostEstimate::FudgeFactor = 1.35;

마찬가지로 선언은 헤더 파일에, 정의는 구현 파일에 위치!


📢 이렇게 끝나면 좋겠지만.... 또 다른 문제가 발생한다.
컴파일 도중 상수의 값이 필요할 때에는 위의 방법들이 통하지 않는다는 것...
이 때에는 enum을 사용 하자!

  • const 보다는 #define에 가까운 동작 방식!
    enum은 define처럼 주소를 얻을 수 없으므로 주소를 얻는다던지, 참조자를 쓴다던지 하는 행동이 필요 없다면 좋은 자물쇠 역할이 될 것이다! 또 한 쓸데없는 메모리 할당도 일으키지 않는다!

  • 상당히 많은 코드에서 쓰이므로 미리 익혀두자!
    enum은 템플릿 메타플그래밍의 핵심 기법이기도 함!

💡 매크로 함수 대신 인라인 함수를 사용하자!

📌 매크로 함수

[ 매크로 함수 정의 ]
#define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b))

  • 원하는 결과를 얻는 정확한 매크로 함수의 구현은 어려우며, 따라서 디버깅 또한 매우 어렵다!

  • 매크로 함수의 길이가 증가할수록 사용되는 괄호 또한 매우 많아져서 가독성이 떨어짐.

📌 인라인 함수, 템플릿

template<typename T>
inline void callWithMax(const T& a, const T& b)
{
	f(a > b ? a: b);
}
  • 매크로의 효율성은 유지!

  • 정규 함수의 모든 동작방식 및 타입 안정성 확보 가능!

profile
오코완~😤😤

0개의 댓글

관련 채용 정보