생성 시 객체 초기화

컴파일러는 객체 생성 시 기반 클래스 및 멤버 데이터의 생성자를 호출하는 코드를 자동으로 생성.

주요 특징

  • 사용자가 생성자 호출 순서를 변경할 수 없음
  • 컴파일러가 생성한 코드는 항상 디폴트 생성자를 호출하므로, 디폴트 생성자가 없는 경우 명시적으로 생성자를 제공해야 함
  • 실제 생성 코드는 Compiler Explorer에서 확인 가능

상속과 멤버 초기화 문제

기반 클래스의 생성자가 파생 클래스의 멤버 초기화보다 먼저 호출되어 발생하는 문제:

  class Stream {
public:
    Stream(Buffer& buf) { 
        buf.use(); 
    }
};

// 문제 코드
class StreamWithBuffer : public Stream {
    Buffer buf{1024};  // 기반 클래스 생성자가 먼저 호출되므로 초기화되지 않은 buf를 사용하게 됨
public:
    StreamWithBuffer() : Stream(buf) {} 
};
  

Base From Member 기법

다중 상속을 이용하여 위 문제를 해결하는 기법:

  class StreamBuffer {
protected:
    Buffer buf{1024};
};

class StreamWithBuffer : public StreamBuffer, public Stream {
public:
    StreamWithBuffer() : Stream(buf) {} 
    // StreamBuffer의 생성자가 Stream보다 먼저 호출되므로 buf가 초기화됨
};
  

Initializer List

C++11에서 도입된 std::initializer_list는 균일한 타입의 값 목록을 함수나 생성자에 전달하는 방법을 제공.

개념

  • const T 타입의 이름 없는 배열을 생성 후, 해당 메모리를 가리키는 객체
  • 읽기 전용으로 사용
  • 함수, 생성자 인자로 많이 사용
  • 사용 이유: 전달된 값의 개수를 쉽게 알 수 있음

생성자와 Initializer List

생성자가 std::initializer_list를 인자로 받을 경우 특별한 규칙이 적용됩니다:

  class Object {
public:
    Object(int a, int b) { /* ... */ }
    Object(std::initializer_list<int> list) { /* ... */ }
};

// Object(initializer_list) 생성자가 호출됨
Object o4{1, 2};  

// initializer_list 객체를 배열처럼 초기화
std::initializer_list<int> list = {1, 2, 3, 4, 5};