#define 대신 const나 enum을 이용하자.

  • #define을 통한 값의 사용은 디버깅시 값의 이름 표기가 아닌 상수 그대로를 나타내기 때문에 코드가 길어지면 해당 값만으로는 분별하기 어려워진다.
  • 상수는 변수의 일종이기 때문에 무분별한 복사가 이루어지지 않지만, define은 사용하는 만큼 복사가 이루어진다.  
  • 전역 상수는 header file에 정의해주는게 일반적인 관례이다. ( 다른 소스 파일이 include 하는 경우 대비 )
  • 전역 상수 포인터는 가리키는 값도 const로 해주자.
    ( ex. const char* const gName = "Fubao Happy"; )
  • 함수처럼 사용되는 매크로를 원하면 #define보다는 인라인 함수를 먼저 검토해보자.

 

 

 

클래스 상수

상수의 복사를 방지하고 하나만 공유하게 하는 멤버 변수이다.

class GamePlayer {
private:
    // gsIndices는 정의가 아닌 선언이다. 
    static const int gsIndices = 5;
    int scores[gsIndices];
}

// 아래와 같이 외부에서 정의를 해주어야한다.
// 보통 정의 부분은 소스 파일 내부에 둔다.
// 상수의 초기값은 해당 상수가 선언되는 시점에 주어지기 때문에 
// 정의에서는 초기값을 주지 않는다. (= 컴파일러마다 다르다. )
const int GamePlayer::gsIndices;

 

 

 

enum을 이용한 방법

class GamePlayer {
private:
    // enum은 동작 방식이 const와 다르다.
    // const 보다는 #define에 가깝다.
    cnum { Indices = 5 }
    int scores[Indices];
}

 

 

 

비트수준 상수성과 논리적 상수성

  • 비트수준 상수성(bitwise constness)
    멤버 함수가 const면 어떤 멤버 변수도 변경되지 말아야한다.
  • 논리적 상수성(logical constness)
    함수가 상수여도 사용자 측에서 알아보지 못하게만 하면 상수 멤버 자격이 있다는 의미

컴파일러는 일반적으로 비트수준 상수성을 준수하므로 mutable을 사용해 수정을 허용하지 않는 이상 데이터의 변경은 어렵다. 

 

 

 

연산자 오버로딩시 const 사용

const char& operator[] ( size_t position ) const
{ return mText[position]; }

char& operator[] ( size_t position ) 
{ return mText[position]; }

Text tb("Hello");
cout << tb[0];  // 상수가 아닌 멤버 호출
tb[0] = 'o';    // 비상수 객체에 write 가능

const Text ctb("World");
cout << ctb[0];  // 상수 멤버 호출
ctb[0] = 'x';    // 반환값이 상수이기 때문에 사용 불가

이는 상수가 아닌 객체와 상수로 된 객체에 대한 호출에 대응하기 위한 방법이다.
만약 중복되는 구현을 피하고 싶다면 아래저처럼 const_cast를 사용해도 된다.  
( 단, 가독성과 효율성 두 가지중 잘 조율해서 판단한다. ) 

 class Text {
 public:
 ...
     const char& operator[] ( size_t position ) const
     {
         ...
         return mText[ position ];
     }
 
     char& operator[] ( size_t position )
     {
         return
             const_cast<char&>( // 반환에 const를 뗀다.
                 static_cast<const Text&> // *this타입에 const를 붙인다.
                 (*this)[position] // 상수 버전의 operator[] 호출
                 );
     }
 }

단, 상수 멤버 함수는 객체내의 데이터 변경을 막는다는 취지이므로 이와 반대로 로직을 구성해서는 안된다.

 

 

 

객체를 사용하기 전에 반드시 객체 초기화 하기

  • 기본제공 타입의 객체는 직접 프로그래머가 초기화해주자. ( 경우에 따라 저절로 되기도 하고 안되기도 하기 때문 )
  • 생성자에서는, 항상 초기화 리스트를 사용하자. 멤버 변수도 선언 순서를 지키고 나열하도록 하자. 
  • 여러 번역 단위에 있는 비지역 정적 객체들의 초기화 순서 문제는 피해서 설계하자. ( 비지역 정적 객체를 지역 정적 객체로 바꾸면 된다. )
  • 멀티스레딩 환경에서는 싱글톤 패턴은 유의하여 사용하자.