Home [C++] Effective C++ CH1
Post
Cancel

[C++] Effective C++ CH1

explicit로 선언된 생성자

  • 예상치 못한 타입 변환을 막아줌
  • 저자는 뚜렷한 이유가 없는 한 생상자를 explicit로 선언한다고 함.

항목 1

  • C++는 다중패러다임 프로그래밍 언어 (절차적 + 객체지향 + functional + generic 프로그래밍 + metaprogramming)

  • C++를 여러 언어의 연합체로 바라보는 것도 좋은 방법(하위 언어)

    • C
    • 객체 지향 개념의 C++
    • 템플릿 C++ (TMP)
    • STL
      • STL 나름 독특한 사용규약이 있음

항목 2

단순한 상수: enum을 우선

매크로 함수 보다는 인라인 함수

  • #define 보다 const, enum, inline
  • 디버거에 이름이 들어가지 않기 때문, 에러 메시지에는 ‘값’만 표시되어 파악하기 힘듦
1
const double ratio = 1.5;
  • define 대신 const 를 사용하면, 최종 코드의 크기가 더 작게 나올 수 있음
    • 매크로는 모두 치환하여 여러 사본이 생김 but 상수는 한 개만

주의

  • 상수 포인터 정의
    • 상수 정의는 대개 헤더 파일에 넣는 것이 상례이므로 아래와 같이 포인터는 const로 선언, 가리키는 대상도 const로 선언하는 것이 보통 (const 두개)

      1
      
      const char * const name = "sm";
      
  • 문자열 상수는 string 객체가 더 나음 (char* 보다)

  • 클래스 상수

    • 정적(static) 멤버로 선언해야함
    • 정적 멤버로 만들어지는 정수류 타입의 클래스 내부 상수는 정의 없이 선언만 해도 아무 문제 없음 (단, 주소를 사용할 경우 정의 필요, 제대로된 컴파일러는 주소를 사용하지 않는한 const 객체에 대해 저장공간을 준비하지 않음,컴파일러에 따라 다름)
    • 정의(구현 파일)에는 상수의 초기값이 있으면 안됨 (상수의 초기값은 선언된 시점에서 주어지기 때문)
  • #define으로 클래스 상수를 만들면 안됨 (유효범위? 매크로는 컴파일 끝날 때까지 유효)

    • enum hack
      • const 보다는 #define 에 가까움 (주소를 취할 수 없는 점, 쓸데없는 메모리 할당 x)
      • 코딩 방식의 결정 => 설계상의 제약을 강화
      • TMP의 핵심 기법
  • #define 매크로 함수 대신 inline 함수에 대한 템플릿 사용

항목 3

const를 붙이면 컴파일러가 사용상의 에러를 잡아내는데 도움을 줌

상수, 비상수 멤버함수의 구현이 같을 때, 코드 중복을 회피하기 위해 비상수 버전이 상수 버전을 호출하도록

1
2
3
4
5
6
char str[] = "hello";

char*p = str; // 비상수 포인터, 비상수 데이터
const char*p = str; // 비상수 포인터 상수 데이터
char* const p = str; // 상수 포인터 비상수 데이터
const char* const p = str; // 상수 포인터 상수 데이터
  • const char*char const * 와 같음

  • const_iterator 는 상수 반복자

함수와 const

  • 함수 반환 값을 상수로

    • 대입연산자 등 실수를 예방 (기본제공 타입과의 쓸데없는 비호환성 회피)
  • 멤버 함수의 const 키워드

    • 해당 멤버 함수가 상수 객체에 대해 호출될 함수라는 것을 알려줌
      • 클래스의 인터페이스를 이해하기 좋게 하기 위해(객체를 변경할 수 있는 함수, 없는 함수를 알려줌)
      • 상수 객체를 사용할 수 있게 해줌
        • 상수 객체에 대한 참조자로 전달해도 조작할 수 있게
    • 오버로딩으로 상수, 비상수 객체에 대해 각각 구현 가능
  • 멤버 함수가 상수 멤버

    • 비트 수준 상수성(bitwise constness, physical constness)
      • 멤버함수가 그 객체를 구성하는 비트들 중 어떤 것도 변경하지 않음.
      • 포인터가 가리키는 대상을 수정하는 멤버함수를 조심해야함 (char * 타입 등)
    • 논리적 상수성(logical constness)
      • 포인터로 인한 상황을 보완
      • 일부 몇 비트 정도는 변경될 수 있지만, 사용자가 모르면 상수 자격이 있음.
      • mutable 키워드를 변수 선언시 사용하면, 그 변수는 const 멤버함수에서 수정 가능 (어떤 순간에도 수정 가능)

상수 비상수 멤버함수 코드 중복 회피

  • 상수 버전과 비상수버전 상황에 따라 캐스팅으로 const 없애는 방식
    • 비상수 가 상수버전을 호출하도록 구현
    • 반대는 권장되지 않음
      • 상수 멤버 함수는 객체의 논리적인 상태를 변경하지 않겠다고 약속한 함수이기 때문
1
2
3
4
5
6
7
8
9
10
11
12
class C{

  const char & operator[] (...) const {}

  char & operator[](...) {
    return const_cast<char&> (
      static_cast<const C&> (*this)[...]
    );
  }
};


항목 4

멤버가 아닌 기본제공 타입객체는 직접 초기화

객체의 모든 부분에 대한 초기화는 멤버 초기화 리스트 사용 및 리스트 나열 순서와 선언 순서와 일치시킴

비지역 정적 객체 초기화 순서 주의 (비지역 정적 객체를 지역 정적 객체로)

  • 객체를 사용하기 전 반드시 초기화
  • 생성자에서 지킬 규칙: 그 객체의 모든 것을 초기화

    • 단, 대입과 초기화는 구별
    • C++ 규칙: 어떤 객체이든 그 객체의 데이터 멤버는 생성자의 본문이 실행되기 전에 초기화되어야함
  • 아래는 대입으로 초기화 단계는 이미 지남
1
2
3
4
5
6
7
8
9
class A{
  int a;
  string b;
};

A::A(int a, string b) {
  m_a = a;
  m_b = b;
}
  • 아래는 초기화 리스트로 초기화함
1
2
3
4
A::A(int a, string b)
  : m_a(a), m_b(b)
{
}
  • 멤버 초기화 리스트

    • 인자들이 바로 데이터 멤버에 대한 생성자의 인자로 쓰임
    • 기본 타입(int 등)은 초기화와 대입에 걸리는 비용의 차이가 없지만, 리스트에 넣어주는 것이 좋음
    • 기본 생성자로 초기화하고 싶을 때도 리스트에 넣어주는 것이 좋은 습관
    • 상수나 참조자는 반드시 리스트로 초기화해야함 (대입 불가능하기 때문)
  • 객체를 구성하는 데이터의 초기화 순서

    1. 기본 클래스는 파생 클래스보다 먼저 초기화
    2. 클래스 데이터 멤버들은 선언된 순서대로 초기화 (초기화 리스트와 선언 순서를 동일하게하는 것은 좋은 습관)

비지역 정적 객체의 초기화 순서는 개별 번역 단위에서 정해진다. => 싱글톤 패턴으로 해결

  • 비지역 정적 객체를 비지역 정적 객체로 초기화 할 때 생기는 문제

    • 서로 다른 번역 단위에 정의된 비지역 정적 객체들 사이의 상대적인 초기화 순서는 정해져 있지 않음.
    • 비지역 정적 객체를 지역 정적 객체로 즉, 함수 내부에 넣고 함수는 그의 참조를 리턴하게 하면 문제는 해결됨
    • 하지만, 초기화 단계에서 멀티스레드 시스템에서는 문제가 생길 수 있음(race condition)
      • 멀티스레드 진입 전 시동 단계에서 전부 초기화하고 진입
  • 지역 정적 객체는 함수 호출 중에 그 객체의 정의에 최초로 닿았을 때 초기화됨

  • 정적 객체

    • 생성 시점부터 프로그램 종료(main 함수 끝) 까지 살아있는 객체
    • 스택 객체 및 힙 기반 객체는 정적 객체가 될 수 없음

      1. 전역 객체
      2. 네임스페이스 유효범위에서 정의된 객체
      3. 클래스 안에서 static으로 선언된 객체
      4. 함수 안에서 static으로 선언된 객체 (지역 정적 객체 local static object)
      5. 파일 유효범위에서 static으로 정의된 객체
    • 비지역 정적 객체(4번 제외)
  • 번역 단위(translation unit)

    • 컴파일을 통해 하나의 목적 파일(object file)을 만드는 바탕이 되는 소스코드
    • include 하는 파일들까지 합쳐서 하나의 번역 단위가 됨
This post is licensed under CC BY 4.0 by the author.

[learn-opengl]Tessellation, Height Maps

[C++] Effective C++ CH2