<개인공부>/[C++]

[C++] 멤버 초기화 리스트 (member initializer lists)

BlockDMask 2021. 2. 19. 00:30
반응형

안녕하세요. BlockDMask 입니다.
오늘은 C++ 멤버 초기화 리스트 라는 주제로 이야기를 해보려합니다.

 

<목차>
1. 멤버 초기화 리스트란?
2. 멤버 초기화 리스트를 꼭 사용해야하는 경우

 

 

1. C++ member initializer lists (멤버 초기화 리스트)


일단 멤버 초기화 리스트 사용방법은 아래와 같습니다.

1. 멤버 초기화 리스트는 생성자 괄호() 뒤에 콜론(:)으로 표기합니다.
2. 초기화 할 멤벼 변수들을 쉼표로 구분하여 표기합니다.
3. 이때 소괄호() 를 이용해서 멤버 변수를 초기화 합니다.
(C++11 이후 부터는 중괄호 초기화, 유니폼 초기화가 가능합니다.)

실제 코드에서 확인해보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
#include<string>
using namespace std;
 
class Car {
private:
    string name;
    int number;
    bool isSuv;
public:
    Car() : name("BlockDMask"), number(1212), isSuv(false)
    {
        //...
    }
};
 
cs

 

Car() : name("BlockDMask"), number(1212
이것처럼 생성자 뒤에 콜론(:)으로 표기하고 멤버 변수들을 초기화 합니다.
또한 각 멤버 변수들 사이는 쉼표로 구분하며 소괄호()를 통해서 멤버 변수를 초기화 합니다.

위와같이 멤버 초기화 리스트를 이용하면 멤버 변수를 생성자가 호출될때 초기화 해줄 수 있습니다.

 

 

이 멤버 초기화 리스트를 사용한것과 사용하지 않은 것의 차이는 "초기화 vs 대입"의 차이가 있습니다.

일반 int 타입의 경우는 이렇게 별 차이가 없어 보입니다.

1
2
3
4
5
6
7
8
9
10
11
int main(void) {
    
    // primitive 타입
    int a = 10// 선언과 동시에 초기화
 
    int b;
    b = 20// 대입
 
    return 0;
}
 
cs

이렇게 선언과 동시에 초기화 하는것과 대입의 차이는 별거 없어 보이지만, 사실 많은 차이가 있습니다.

이걸 클래스에서 보면 한눈에 차이가 보일 것 입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include<iostream>
#include<string>
using namespace std;
 
class Person {
private:
    int age_;
    string str_;
public:
    Person() : age_(0), str_("BlockDMask")
    {
        cout << "디폴트 생성자 호출" << endl;
    }
 
    Person(int age, string str) : age_(age), str_(str)
    {
        cout << "age, str 생성자 호출" << endl;
    }
    
    Person& operator = (const Person& rsh)
    {
        this->age_ = rsh.age_;
        this->str_ = rsh.str_;
        cout << "대입 연산자 호출" << endl;
        return *this;
    }
 
    void printAll() {
        cout << "age : " << age_ << endl;
        cout << "str : " << str_ << endl;
    }
};
 
int main(void) {
    // 클래스 같은 경우
    cout << "\n=====\n";
    Person p1(10"aa");         // age, str 생성자 호출
    p1.printAll();
    
    
    cout << "\n\n=====\n";
    Person p2 = { 20"bb" };    // age, str 생성자 호출
    p2.printAll();
 
 
    cout << "\n\n=====\n";
    Person p3 = Person(30"cc"); // age, str 생성자 호출
    p3.printAll();
 
 
    cout << "\n\n=====\n";
   Person p4;             // 디폴트 생성자 호출
    p4 = Person(40"dd"); // age, str 생성자 호출, 대입 연산자 호출
    p4.printAll();
 
    int z;
    cin >> z;
 
    return 0;
}
 
cs

초기화와 대입의 차이

이렇게 p1, p2, p3은 생성과 동시에 초기화를 하기 때문에 age, str 생성자 한번만 호출되게 되는데

p4의 경우 
Person p4; 할때 디폴트 생성자가 호출되고
p4 = Person(40, "dd"); 일때 age, str 생성자가 호출되면서 임시객체가 만들어지고, 그 객체를 p4에 대입연산자를 통해서 대입이 되는 그런 형태가 됩니다.

이렇게 초기화와 대입이 코드 한 줄 차이밖에 없는데 참 많이 다른것을 알 수 있습니다.

 

이걸 이번에시간에 배우는 주제인 멤버 초기화 리스트와 비교해보자면 
아래와 같은 멤버 초기화 리스트는 
int a = 10; 과 같이 선언과 함께 초기화 하는것과 같다고 말할 수 있고,

1
2
3
4
Person(int age, string str) : age_(age), str_(str)
{
    //...
}
cs

 

선언 후에 대입 하는 식은 아래와 같다고 할 수 있습니다.

1
2
3
4
5
Person(int age, string str)
{
    age_ = age;
    str_ = str;
}
cs

 

초기화의 대입의 차이를 이제 이해가 가셨나요?

 

 

 

2. C++ 멤버 초기화 리스트를 꼭 사용해야하는 경우.


반드시 멤버 초기화리스트를 사용해야 하는 경우는 선언과 동시에 초기화 해야하는 변수들이 있습니다.

const 변수나 reference 변수의 경우 선언과 동시에 초기화 해야하므로,
class 내부의 멤버 변수로 const, reference 변수가 있는 경우에는 멤버 초기화 리스트 문법을 사용해야지 선언과 동시에 초기화가 가능해집니다.

위에서 언급했듯이 멤버 초기화 리스트는 말그대로 "초기화" 입니다.

const 변수의 경우
const int a = 10; 이와 같이 선언과 동시에 초기화가 되어야 합니다.

cosnt int b;
b = 20;
이렇게 대입을 통해서 할 수 없습니다. 그쵸?

이게 멤버 초기화 리스트에서도 똑같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person {
private:
    const int age;
    bool isMan;
public:
    // 아래와 같이 멤버 초기화 리스트는 const 변수 가능
    Person() : age(100), isMan(true)
    {
        //const 변수를 대입, 할당 하는것 이기 때문에 불가능
        //age = 99;            //error
        //isMan = false;    //ok
    }
};
cs

이런식으로 멤버 초기화 리스트에서는 const 변수를 초기화 가능하지만,
생성자 내부에서 age = 99; 와 같이 대입은 불가능 하게 됩니다.

 

이처럼 멤버 초기화 리스트에서만 가능한 것이 있기도 하고, 
int, bool 과 같이 primitive 자료형에서는 초기화, 대입의 성능차이가 크지는 않지만,
사용자가 정의한 타입의 경우에는 초기화, 대입의 비용이 많이 차이가 날 수 있습니다.

그러므로, 우리 모두 멤버 초기화 리스트를 애용합시다.

 

이상으로 C++ 멤버 초기화에 대한 포스팅을 마치겠습니다.
감사합니다.

반응형