Skip to content

2014 06 04 디폴트 생성자, 멤버 초기화 순서, 복제 생성자

Jaesoo Lim edited this page Jan 15, 2015 · 2 revisions

명시적인 디폴트 생성자

기존에는 디폴트 생성자에서 아무런 일도 해주지 않아도 선언부와 정의부를 분리하기 위해서는 거추장스럽게 코드를 해야 했습니다.

class MyClass {
 public:
  MyClass();    // 선언부
  MyClass(int i);
};

MyClass::MyClass() {}    // 정의부

그러나, C++11부터는 아래와 같이 간편하게 디폴트 생성자를 명시적으로 선언해 줄 수 있습니다.

class MyClass {
 public:
  MyClass() = default;    // 명시적인 디폴트 생성자
  MyClass(int i);
};

명시적으로 삭제된 디폴트 생성자

때로는 디폴트 생성자를 자동으로 만들지 않도록 해주고 싶을 경우가 있습니다. C++11에서는 아래와 같이 해주면 됩니다.

class MyClass {
 public:
  MyClass() = delete;    // 명시적으로 삭제된 디폴트 생성자
};

멤버 초기화 순서

생성자에서 나열해준 순서는 의미 없고, 클래서 선언부에서 나열된 순서대로 멤버가 초기화 된다.

class MyClass {
 public:
  MyClass();
 private:
  int m1;
  int m2;
};

MyClass::MyClass() : m2(1), m1(m2 + 1) {}

위 코드에서 m2가 1로 초기화 되고 m1이 m2 + 1로 초기화 될 것 같지만, 실상은 m1이 초기화되지 않은 garbage로 채워진 m2에다 1을 더해 먼저 초기화 되고, 나중에 m2가 1로 초기화 됩니다. 그런데 이 부분은 너무 걱정 안하셔도 됩니다. 컴파일러가 경고를 내줄 테니까요. ^^;

복제 생성자의 명시적인 자동생성과 자동생성 방지

디폴트 생성자와 마찬가지로 복제 생성자도 아래와 같이 명시적으로 지정해 줄 수 있습니다.

class MyClass {
 public:
  MyClass(const MyClass& that) = default;    // 명시적인 자동생성
  MyClass(const MyClass& that) = delete;     // 자동생성 방지
};

참고로, 기존에는 복제 생성자 및 대입 연산자는 방심하면 이로 인해 낭패를 보는 경우(특히, 포인터 값이 복제되어 double free를 유발하는 경우)가 있어서 아래와 같이 private으로 선언해 주어 의도치 않게 사용될 경우 컴파일할 때 오류가 나도록 하는 방법이 있습니다.

class MyClass {
 private:
  MyClass(const MyClass& that);    // 복제 생성자
  MyClass& operator=(const MyClass& that);    // 대입 연산자
};

그래서 아래와 같은 매크로를 만들어서 모든 클래스에서 복제 생성자 및 대입 연산자를 기본적으로 막아두는 것이 안전합니다.

#define DISALLOW_COPY_AND_ASSIGN(T) private: T(const T&); T& operator=(const T&);

class MyClass {
  DISALLOW_COPY_AND_ASSIGN(MyClass);
};
Clone this wiki locally