초창기의 C++은 단순히 C언어에 객체 지향 기능 몇 가지가 결합된 형태
초창기 이름도 클래스를 쓰는 C(C with Classes)
꾸준히 성장한 C++은 다중 패러다임 프로그래밍 언어가 됨
C++을 잘 이해하기 위해서는 한 가지 프로그래밍 규칙 아래 뭉친 통합 언어가 아니라 4가지 하위 언어들의 연합체로 이해하자
"가급적 선행 처리자보다 컴파일러를 더 가까이 하자"
#define ASPECT_RATIO 1.653
이 코드는 컴파일러에게 넘어가기 전에 선행 처리자가 ASPECT_RATIO를 숫자로 바꿈
상수로 대체된 코드에서 컴파일 에러가 발생하면 원인을 꽤나 헷갈릴 수 있음
해결법은 매크로 대신 상수를 사용
const double AspectRatio = 1.653;
AspectRatio는 언어 차원에서 지원하는 상수 타입이기 때문에 기호 테이블에 들어가 디버깅하기 편함
매크로를 쓰면 ASPECT_RATIO가 등장하기만 하면 선행 처리자가 모두 1.653으로 바꾸면서 목적 코드 안에 1.653의 사본이 등장 횟수만큼 들어가기 때문에 최종 크기가 큼
상수 타입의 AspectRatio는 여러 번 쓰더라도 사본은 한 개만 생기기 때문에 최종 크기가 작음
클래스 컴파일 도중에 상수의 값이 필요할 경우 '나열자 둔갑술(enum hack)'을 사용하자
enum은 #define처럼 어떤 형태의 메모리 할당을 저지르지 않음
class temp
{
const int a = 10; // error
enum {a = 10}; // ok
int data[a];
};
함수 매크로 대신에 정규 함수의 모든 동작 방식 및 타입 안전성까지 취할 수 있는 inline template 함수를 사용하자
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))
위 코드는 아래의 경우 예상치 못한 동작을 함
int a = 5, b = 0;
CALL_WITH_MAX(++a, b); // a is incremented twice
그래서 아래와 같이 inline template 함수를 사용하는 것이 훨씬 안전함
template<typename T>
inline void callWithMax(const T& a, const T& b)
{
f((a > b) ? a : b);
}
const를 붙여 선언하면 컴파일러가 사용상의 에러를 잡아내는데 도움을 줌
아래 코드에서 상수를 수정하려고 한다면 컴파일러가 에러를 발생함
char greeting[] = "Hello";
char *p = greeting; // 비상수 포인터, 비상수 데이터
const char *p = greeting; // 비상수 포인터, 상수 데이터
char * const p = greeting; // 상수 포인터, 비상수 데이터
const char * const p = greeting; // 상수 포인터, 상수 데이터
컴파일러 쪽에서 보면 비트 수준 상수 성을 지켜야 하지만 프로그래머는 개념적인(논리적인) 상수 성을 지키며 프로그래밍해야 함
class temp
{
char& operator[](size_t p) const
{
return pText[p];
}
private:
char *pText;
};
const temp t("Hello");
char *pc = &t[0];
*pc = 'h';
위 코드를 보면 operator[]함수는 상수 멤버 함수로 비트수준 상수성을 만족시켜 컴파일러는 에러를 발생하지 않음
하지만 그 주소를 이용해 데이터 수정이 가능하기 때문에 프로그래머는 논리적 상수성을 지키며 프로그래밍 해야 함
기본 제공 타입의 객체는 직접 손으로 초기화 하자.
경우에 따라 저절로 되기도 하고 안되기도 하기 때문
생성자에서는 데이터 멤버에 대한 대입문을 생성자 본문 내부에 넣는 방법으로 멤버를 초기화하지 말고 멤버 초기화 리스트를 사용하자.
기본 생성자 호출 후에 복사 대입 연산자를 연달아 호출하는 방법보다 복사 생성자를 한번 호출하는 쪽이 더 효율적이기 때문
서로 다른 소스 코드에 정의된 정적 객체들 사이의 상대적인 초기화 순서는 정해져 있지 않음을 항상 염두해 두자.