항목 1. C++를 언어들의 집합체로 바라보는 안목은 필수

  • 다중 패러다임 언어 : 절차적, 객체지향, 함수식, 일반화, 메타 프로그래밍
  • 하위 언어 : C, C++, 템플릿 C++, STL

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

  • 선행 처리자보다 컴파일러를 더 가까이 하자.
  • 컴파일러로 넘어가기 전에 선행처리자는 숫자 상수로 대체되므로 컴파일러 기호 테이블에 선행처리자 상수 이름은 들어가지 않는다.
  • const로 대체 가능

  • 정의 시 주의점
    1. 상수 포인터로 정의
    2. 클래스 멤버로 상수 정의(클래스 상수)(사본 갯수를 한개 이상으로 하기 싫으면 static 멤버로)
  • 선언 / 정의 : 사용하고자 하는 것에 대해서는 정의가 마련되어 있어야 한다.(정적 멤버로 만들어지는 정수류 타입 클래스 내부 상수 제외)

  • 선행 처리자는 유효 범위가 없어서 클래스 상수에 활용할 수 없다.

  • 해당 클래스를 컴파일하는 중 클래스 상수의 값이 필요할 땐?
  • 선언시 상수 값을 넣지 못하는 구식 컴파일러를 사용할 시
  • 나열자 둔갑술(enum hack) : 나열자는 int 위치에서 쓰일 수 있음을 활용

    ex) 클래스 내부에 enum { numTurns = 5 } 식으로 선언

    1. const보다 #define에 가깝다.
      • const 주소를 취하는건 합당하지만 enum, #define은 불법
      • 안 좋은 컴파일러는 const 객체의 저장공간을 준비할지도 모른다.
    2. 많은 코드에서 쓰이므로 눈을 단련(템플릿 메타 프로그래밍의 핵심)
  • 선행 처리자의 또 다른 오용 사례 : 매크로 -> 함수 호출을 없애주지만 오류 / 가독성 문제
    • 인라인 함수에 대한 템플릿 : 매크로 효율을 유지하며 타입안정성도 있다.
  • 인라인 함수에 대한 템플릿(inline)
    1. 매크로의 단점인 괄호 남발, 인자 여러번 평가가 없다.
    2. 진짜 함수라서 유효 범위 및 접근 규칙을 그대로 따른다.

항목 3. 낌새만 보이면 const를 들이대보자!

  • const
    1. 의미적 제약(외부 변경 불가능)을 소스코드 수준에서 붙인다.
    2. 컴파일러가 이 제약을 단단히 지켜준다.
  • 사용 범위
    1. 전역, 네임 스페이스 유효 범위의 상수를 선언(정의)
    2. 파일, 함수, 블록 유효 범위에서 static 선언 객체에 사용 가능
    3. 클래스 내부 정적/ 비정적 데이터 멤버 사용 가능
    4. 포인터 자체/ 포인터가 가리키는 데이터 상수 지정 가능 -> const(포인터가 가리키는 대상) * const(포인터 자체)
      • STL 반복자는 포인터를 본뜬 것이므로
        • const iterator = T* const
        • const_iterator = const T*
    5. 함수 선언에 사용 가능 : 함수 반환값, 각각의 매개변수, 멤버함수, 함수 전체
      • 함수 반환값을 상수로 : 안전성 / 효율을 포기하지 않고 사용자 측 에러 감소
      • 매개변수 / 지역객체 수정이 필요 없을 시 const 선언 잊지 말자
      • 상수 멤버 함수(멤버 함수에 붙는 const) : 해당 멤버가 상수 객체에 대해 호출될 함수임을 표기
  • 중요한 이유
    1. 클래스의 인터페이스를 이해하기 좋게 하기 위함(사용자가 객체 변경 가능 함수 / 불가 함수를 인지해야 한다.)
    2. 상수 객체를 사용할 수 있게 하기 위함(코드 효율) -> 객체 전달을 상수 객체에 대한 참조자로 진행할 때 필요.
  • const 키워드 유무만으로도 오버로딩 가능

  • 실 프로그램에서 상수 객체가 생기는 경우
    1. 상수 객체에 대한 포인터로 객체가 전달될 때
    2. 상수 객체에 대한 참조자로 객체가 전달될 때
  • 어떤 멤버 함수가 상수 멤버(const)라는 의미는?
    1. 비트 수준 상수성(bitwise constness)(=물리적 상수성(physical constness))

      : 어떤 멤버 함수가 그 객체의 어떤 데이터 멤버도 건드리지 않아야 const 인정. C++에서 정의하고 있는 상수성. 포인터가 가리키는 대상을 수정할 시 문제가 된다.

    2. 논리적 상수성(logical constness)(이걸 지켜야 한다)

      : 일부 몇 비트 정도는 바꿀 수 있되 그걸 사용자측에서 알아채지 못하면 cosnt 인정

  • 논리적 상수성을 위해 내부에서 비트 수정을 위해 mutable 키워드(어느 순간에도 수정 가능하게 하는 키워드)를 사용하여 변수 선언을 할 수 있다.

  • 상수 멤버/ 비상수 멤버 함수가 기능적으로 같을 시 코드 중복을 피하기 위해 비상수 버전이 상수 버전을 호출하게 만들자.
    • ex)
return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);

char& operator에서 상수 멤버 함수를 호출하기 위해 static_cast, 리턴값의 const를 떼어내기 위해 cosnt_cast 해주었다.

  • 상수 버전이 비상수 버전을 호출하는 것의 문제점
    1. 상수 버전에서는 비상수 멤버를 호출할 수 없으므로. 혹여 수정되는(상수성을 위배하는) 동작이 포함될 수 있다.
    2. *this의 const를 const_cast를 적용하는 건 재앙의 씨앗이 된다…