본문 바로가기

Programming/C&CPP

C++ 클래스 생성자에서 초기화 리스트 사용해야하는 경우

반응형

C++에서는 클래스를 생성하는데 호출되는 생성자라는 특수한 형태의 함수가 있습니다.

class A{}; 가 정의되어 있을때 생성자의 이름은 A(){}이고, 리턴 타입은 존재하지 않습니다.

생성자는 오버로딩이 가능합니다.

즉, 넘겨줄 인자의 형식을 달리해서 다양한 생성자를 둘 수 있습니다.

또한 생성자는 보통 다음과 같이 표현됩니다.

A(int a, int b)
{
        m_a = a;
        m_b = b
};

그런데 이런 방식 말고 초기화 리스트(Member Initialization List)를 사용해서

클래스를 초기화할 수 있습니다.

초기화 리스트는 다음과 같이 표현됩니다.

A(int a, int b) : m_a(a), m_b(b)
{
        //더 할 일
};

되도록이면 초기화 리스트를 사용해서 초기화하는 것을 권장합니다.

또한 초기화 리스트를 반드시 사용해야만 하는 경우가 존재합니다.

다음의 4가지 경우는 초기화 리스트를 반드시 사용해야하는 경우입니다.

1. 상수 멤버 변수 초기화

변수를 선언할 때, const가 붙으면 초기에 정의할 때 말고는 절대 변경이 안됩니다.

즉 const int a = 365; 인데 const int a; 라고만 하면

Visual Studio에서는 다음과 같은 에러를 뱉어냅니다.

error C2734: 'a' : const object must be initialized if not extern

해석해보면 extern이 아니라면 const는 반드시 초기화가 되어야 한다는 말입니다.

class 내부의 const 멤버에 대해서 초기화를 할 때 초기화 리스트를 사용합니다.

class를 정의한 것은 모양만을 명시할 뿐이지, 메모리를 할당하지는 않습니다.

그래서 class내에서 직접 변수에 바로 값을 넣는 것은 불가능합니다.

(C++11 이후에는 class 내에서 직접 값을 넣는 것이 가능해졌습니다.)

이럴 때 이용하는 것이 초기화 리스트입니다.

정리하면 다음과 같습니다. 

class ABC
{
public:
        const int A;
        ABC(int i) : A(i){}
};

이런식으로 초기화를 진행하면 됩니다.

2. 레퍼런스 멤버 변수 초기화

레퍼런스는 변수에 대한 또다른 이름이기 때문에 반드시 그 대상이 존재해야 합니다.

레퍼런스는 생성시에 짝이 있어야 합니다.(예외도 존재)

그걸 해주는 것이  초기화 리스트입니다.

class ABC
{
public:
        int& A;
        ABC(int i) : A(i){}
};

변수 선언만 위랑 조금 달라졌을 뿐, 형식은 같습니다.

만약, 이 방법으로 초기화하지 않을 경우는 다음과 같은 오류를 뱉어냅니다.

error C2758: 'A' : must be initialized in constructor base/member initializer list

A라는 것을 반드시 생성자의 초기화 리스트에서 초기화하라는 오류를 출력시킵니다.

3. 포함된 객체의 초기화

class의 내부에 멤버 변수로 다른 클래스의 인스턴스를 포함하고 있다면 어떨까요?

다음과 같이 해줘야 합니다.

class A
{
public:
        int x, y;
        A(int a, int b){x = a; y = b;};
};

class B
{
public:
        A a;
        B(int x, int y) : a(x, y){}
        void PrintValue()
        {
                std::cout << a.x << a.y ;
        }
};

생성자는 객체가 생성될 때 자동으로 호출되기 때문이죠.

B(int x, int y){A a(x, y)} 이런 식으로 호출하면

그냥 지역 변수가 하나 생성될 뿐 아무런 의미가 없습니다.

그렇기 때문에 초기화 리스트를 사용해서 초기화 해야합니다.

4. 상속 받은 멤버 변수를 초기화 할 때

상속받은 멤버는 자식 클래스에서 직접 초기화할 수 없기 때문에

이 작업을 부모에게 요청해야 합니다.

이럴 때도 역시 초기화 리스트를 사용해서 합니다.

class Parent
{
public:
        int P1, P2;
        Parent(int i, int j)
        {
                P1 = i;
                P2 = j;
        }
};

class Test : public Parent
{
public:
        int T3;
        Test(int i, int j, int k) : Parent(i, j)
        {
                T3 = k;
        }
};

이런 식으로 초기화해야 하는 것입니다.

이렇게 4가지 경우가 존재합니다.

이것으로 생성자의 초기화 리스트를 반드시 사용해야 하는 상황에 대한 설명을 마칩니다.

반응형