Even Idiots Can Make Game

생성자와 멤버 초기화 목록

Date/Lastmod
Section
DEV
Categories
C++

생성자는 특수한 선언 문법과 함께 선언되는 비정적 멤버 함수로, 해당 클래스 형식의 객체를 초기화하는 데에 사용된다.

클래스-이름(파라미터-목록)

생성자는 이름이 없으며 직접 호출할 수 없다.

생성자는 초기화가 필요한 곳에서 호출되며, 초기화 규칙에 따라서 적절한 것이 선택된다.

explicit 키워드가 없는 생성자는 변환 생성자라고 부른다. constexpr 키워드가 없는 생성자는 형식을 리터럴 형식으로 만든다.

아무런 인자 없이 호출되는 생성자를 기본 생성자(Default Constructor)라고 부른다.

같은 형식의 다른 객체를 인자로 받는 생성자를 복사 생성자(Copy Constructor), 혹은 이동 생성자(Move Constructor)라고 부른다.

생성자 함수의 본문이 실행되기 전에:

멤버 초기화자 목록은 이러한 서브 객체들을 기본값이 아닌 값으로 초기화할 수 있는 곳이다. 멤버 초기화자의 문법은 다음과 같다.

클래스-혹은-식별자(표현식-목록)  // (1)
클래스-혹은-식별자 중괄호-초기화-목록 // (2)
파라미터-팩... // (3)
struct S {
  int n;
  // [생성자 선언]
  S(int);
  // [생성자 정의]
  // `: n(7)`은 초기화자 목록
  // `: n(7) {}`은 함수 몸체
  S(): n(7) {}
};

// [생성자 정의]
// `: n{x}` 는 초기화 목록
S::S(int x): n{x} {}

int main() {
  S s1; // S::S()를 호출함
  S s2(10); // S::S(int)를 호출함
}

다음의 경우 반드시 멤버 초기화자를 명시해야 한다:

가상 기본 클래스와 관련된 초기화자: 클래스-혹은-식별자가 가상 기초 클래스를 이름으로 지칭하는 초기화자는, 현재 생성 중인 객체가 가장 파생된 클래스(Most Derived Class)가 아닌 경우 무시된다.1

초기화자 표현식 내의 이름 평가: 초기화자의 표현식 목록 또는 중괄호 초기화 목록 내에 나타나는 이름들은, 해당 생성자의 범위 내에서 평가된다. 2

#1 객체의 생성

유도 클래스의 생성자가 호출될 때, 기초 클래스의 기본 생성자가 자동으로 호출된다.

기초 클래스의 기본 생성자가 존재하지 않거나, 자동 생성할 수 없고, 혹은 접근할 수 없는 경우 컴파일 오류가 발생한다.

다음 코드가 왜 말이 안 되는 코드인지 짚어낼 수 있어야 한다.

class Super {
public:
  int i_;
};

class Derived: public Super {
public:
  Derived(const int& i): 
    i_(i) 
  {}
};

#2 멤버 초기화자 목록

생성자의 함수 본문 중괄호가 시작되기 이전에, 멤버 초기화자 목록이 올 수 있다.

C++의 멤버 초기화자 목록에서는 오직 자신의 클래스에 선언된 멤버 변수만 직접 초기화 할 수 있다. (물론 생성자 위임도 가능하다)

부모 클래스의 멤버에 대해서는 부모 클래스의 생성자에게 초기화를 맡겨야 하며, 직접 멤버를 초기화 할 수는 없다.

(초기화자 목록에 등장하는 이름들은 생성자의 스코프 내에서 평가된다.)

i_Derived의 멤버가 아니기 때문에 Derived의 멤버 초기화자 목록에서 호출할 수 없다.

객체의 생성 과정은 다음과 같이 3단계임

  1. 메모리 공간 할당
  2. 이니셜라이저를 이용한 멤버 변수 초기화
  3. 생성자의 몸체부({}) 실행

2번을 제외하면 1, 3은 필수적인 과정임. 특히, 3의 경우 생성자가 삽입되어 이루어지는 과정임

가상 함수 테이블은 생성자가 끝난 후에나 완벽히 설정되므로, 부모의 가상함수 테이블을 기반으로 작동함. 그러므로 생성자 내에서 가상 함수를 호출하는 것이 무슨 의미인지 알지 못한다면, 호출하지 않는 것이 좋음

2번은 명시하지 않으면 암시적으로 작성된다고 보면 됨

멤버 이니셜라이저에서 우선 기초 클래스의 생성자가 명시되었든 안 되었든 호출되려고 함. 호출이 어떤 이유에서든 불가능한 경우, 컴파일 오류가 발생함. 그리고 클래스의 각 멤버에 대해서, 클래스 타입의 경우 명시한 생성자가 호출되어 초기화되거나, 암시적으로 기본 생성자가 호출됨. (이 역시 암시적으로 호출되는 경우 기본 생성자에 접근이 불가능하면 컴파일 오류가 발생함) 기본 타입의 경우 명시한 값으로 초기화 되나, 명시하지 않은 경우 쓰레기값이 들어감.

C++11 부터 도입된, 클래스 멤버에 직접 초기화를 하는 것은 멤버 이니셜라이저와 동일하게 작동함. C++ 코어 가이드라인에는 클래스 멤버에 직접 초기화를 명시하는 것을 추천함

#1 참고 문헌

부모 클래스의 멤버에 대해서는 부모 클래스의 생성자에게 초기화를 맡겨야 하며, 직접 멤버를 초기화 할 수는 없다.


  1. 가상 상속 계층에서는 가장 파생된 클래스만 가상 기초 클래스를 초기화할 수 있다. 중간 클래스에서의 가상 기초 클래스 초기화는 무시된다. ↩︎

  2. 초기화자 내에서 사용되는 모든 이름과 표현식은 해당 생성자의 범위 내에서 평가되므로, 해당 클래스 멤버와 접근 가능한 범위의 이름들만 사용할 수 있다. ↩︎

comments powered by Disqus