이 글은 'Effective C++' 을 읽고 정리한 내용입니다.
"선행 처리자보다 컴파일러를 더 가까이하자."
#define
- #define은 소스코드가 어떻게든 컴파일러에게 넘어가기 전에 선행 처리자가 밀어버리고 '숫자 상수로' 바꾸어버린다.
문제점
- #define으로 정의된 코드에서 에러가 발생하게되면, ASPECT_RATIO가 아닌, 1.653으로 에러메세지가 발생하게 된다.
- 위와 같은 경우에는 코드에서 1.653이 의미하는 바가 무엇인지 모를 경우에 곤란한 상황이 생긴다.
const
위와같은 문제를 해결하는 방법으로 const를 이용하는 방법이 있다.
장점
- AspectRatio는 언어 차원에서 지원하는 상수 타입의 데이터이기 때문에 컴파일러에서 처리된다.
- 상수가 부동 소수점 실수 타입일 경우 #define을 썼을 때보다 작게 나올수있다.
그 이유는 #define으로 처리된 코드는 ASPECT_RATIO가 등장만하면 선행 처리자에 의해 1.653으로 바꾸기 때문에,
결국 사본이 등장 횟수만큼 들어가게 되지만, 상수타입의 AspectRatio는 여러번 쓰더라도 사본은 딱 한 개만 쓰인다.
#define을 상수(const)로 바꿀 때 주의할점
1. 상수 포인터를 정의하는 경우
- 상수 pointer는 꼭 const로 선언해 주어야 하며, 추가로 포인터가 가리키는 대상까지 const로 선언하는 것이 보통이다.
( const pointer에 대한 추가 내용(바로가기) )
- cf) 문자열 상수를 쓸땐 char * 보단 string 객체가 대체적으로 사용하기 좋다.
2. 클래스 멤버로 상수를 정의하는 경우( 클래스 상수를 정의하는 경우 )
- 클래스 멤버로 상수를 정의하는 경우, 그 상수의 사본 개수가 한 개를 넘지 못하게 하기 위해선 정적(static) 멤버로 만들어야한다.
- 위에있는 코드의 NumTurns는 '선언' 된것이다. '정의'가 아니다.
- 정적멤버로 만들어지는 정수류 타입은 정의가 없어도 컴파일러의 오류가 없다.
- 만약 컴파일러의 이상으로 정의가 필요하다고 하면 헤더파일이 아닌 구현 파일에 다음과 같은 정의를 제공 해야한다.
- 정의에는 상수의 초기값이 있으면 안된다. 클래스 상수의 초기값은 해당 상수가 선언된 시점에서 바로 주어지기 때문.
위 경우도 받아들여지지 않는 컴파일러가 있다 이럴 때는 초기값을 상수 '정의'시점에 주면된다.
나열자 둔갑술( enum hack )
- 이 책의 경우는 위에 말한 경우를 제외하고, 클래스를 컴파일하는 도중에 클래스 상수의 값이 필요할 때인데, 배열 멤버를 선언할 때의 경우, 배열의 크기를 알아야 한다며 정적 타입의 정적 클래스 상수에 대한 클래스 내 초기화를 금지하는 에러를 배출하는 경우 나열자 둔갑술을 사용하여 해결 할 수있다.
- 나열자 둔갑술의 동작 방식은 const 보단 #define에 가깝다.
- 선언한 정수 상수를 가지고 다른 사람이 주소를 얻는다든지 참조자를 쓴다든지 하는 것이 싫다면 enum을 사용하라.
- enum은 #define처럼 어떤 형태의 쓸데없는 메모리 할당을 저지르지 않는다.
- 상당히 많은 코드에서 이 기법이 사용 되고 있으므로 실용적인 이유에서 라도 눈에 익혀 놓는 것이 좋다.
inline 함수
- #define을 잘못 사용하는 것중 하나는 매크로 함수이다. 함수 처럼 보이지만 함수 호출 오버헤드를 일으키지 않는다.
- 위와 같은 매크로 정의는 많은 문제를 야기한다
- 이 문제를 해결하기 위해 인라인 함수에 대한 템플릿을 준비한다.
- 위 함수는 템플릿이기 때문에 동일 계열 함수군을 만들어 낸다.
- 동일한 타입의 객체 두 개를 인자로 받고, 둘중에 큰 것을 f에 넘겨서 호출하는 구조.
- callWithMax는 함수이기 때문에 유효범위 및 접근 규칙을 그대로 따라간다.
결론
- 단순히 상수를 쓸때는 #define보다 const객체나 enum을 우선 생각하자.
- 함수처럼 쓰이는 매크로를 만들려면 #define 매크로보다 인라인 함수를 먼저 생각하자.
'개발 > C++' 카테고리의 다른 글
안전한 정수 연산을 위해 SafeInt 를 써보자 (0) | 2020.07.13 |
---|---|
VS 2017 환경에서 glog 설치 및 적용 방법 (0) | 2020.07.09 |
[EC++] 용어 정리 ( 선언, 정의, 기본생성자, 복사생성자, 초기화, 복사대입연산자 등 ) (0) | 2020.02.10 |
[C++] const 포인터 (0) | 2020.02.07 |
Array와 List 비교 (0) | 2020.02.06 |
댓글