본문 바로가기

복습/C++

[C++] 복습일지 part 2 - 4 #멤버 이니셜라이저

# 상속 (Inheritance)

# 오버로딩(Overloading)과 오버라이딩(Overriding)

# 가상함수, 순수가상함수 (Virtual Function, Pure Virtual Function)

# 멤버이니셜라이저 (Memeber Initializer)





#멤버 이니셜라이저(Member Initializer)


여기에 한 코드가 있다.



#include <iostream>

int main()
{
    int num1 = 10;
    int num2(20);
    
    std::cout << "num1 : " << num1 << std::endl;
    std::cout << "num2 : " << num2 << std::endl;
    return 0;
}
</span>



출력 결과는?



6행에서 num2(20) 을 했다.

신기하게 생겼다. 하지만 그냥 간단하게 생각하면


'num2를 20으로 초기화'


라는 뜻이다.


이번엔 다른 코드를 한번 보자



#include <iostream>

class A
{
public:
    A()
    {
        a = 1;
        b = 1;

    }
private:
    const int a;
    const int b;
};
int main()
{
    A a;
    return 0;
}
</span>

이 코드는 컴파일이 되는가?


컴파일 에러!


이유는 무엇일까?

바로 const인 int형 a의 값을 1로 바꾸려했기 때문이다.


이걸 그럼 어떻게 초기화를 할까?


바로 멤버 이니셜라이저를 사용하면된다

개선된 코드를 보겠습니다.



#include <iostream>

class A
{
public:
    A() : a(1), b(1)
    {
        std::cout << a << std::endl;
        std::cout << b << std::endl;
    }
private:
    const int a;
    const int b;
};
int main()
{
    A a;
    return 0;
}


6행을 주목하면된다!


6행에서 A클래스의 기본 생성자로 const인 int형 값들을 초기화 했다.!

컴파일 에러는 먹지 않는다.

출력결과를 본다면


이제 풀이를 하자면


아까 

int num2(20);

구문이 생각 난다면 쉬울것이다.

말로 풀어서 설명을하면,


'num2'를 '20'으로 초기화


멤버 이니셜라이저도 마찬가지이다

A() : a(1), b(1)

{

}

a를 1로 초기화, b를 1로 초기화



여기서 알아야할 점이 또 하나있다.

바로 선언 순서대로 초기화가 진행된다는 점이다.

코드를 보겠습니다.


#include <iostream>

class A
{
public:
    A(int a, int b) : y(b), x(y)
    {
    }
    const int x;
    const int y;
};
int main()
{
    A a(3, 3);
    std::cout << "a.x = " << a.x << std::endl;
    std::cout << "a.y = " << a.y << std::endl;
    
    return 0;
}


코드를 해석하자면,

const int x;

const int y;

1. x가 y보다 먼저 선언이 되었으므로 먼저 초기화가 진행이 된다.

2. x(y)  => x를 y로 초기화 

    y가 무엇인지 모르므로 x가 쓰레기값으로 초기화

3. y(b) => y를 b로 초기화

    b가  3으로 들어왔으므로 y가 b로 초기화


4. main()에서의 출력값



x에 쓰레기값으로 초기화가 되어 정상적으로 출력되지않는다.

이 예제를 통해서 

변수의 선언 순서에따라 멤버이니셜라이저의 순서가 결정된다는것을 알았다.




마지막으로 알아야할 점이있습니다. 

참고 : http://insidecoding.blogspot.kr/2015/04/member-initialiser.html

바로, 상속관계에서 생성자의 호출 순서가 부모->자식 순서로 된다고 말씀드렸었는데요

더 깊게 들어가자면, 순서는 이렇습니다.


1. 자식의 생성자 호출

2. 기계어가, 자식생성자에 부모의 생성자를 멤버 이니셜라이저 과정을 코드에 삽입

3  부모의 생성자 호출

4. 자식 생성자 멤버 이니셜라이저



코드를 먼저 보겠습니다.




#include <iostream>

class Point
{
public:
    Point(int _x, int _y) : x(_x), y(_y) {
        std::cout << "부모" << std::endl;
    }
    
private:
    const int x;
    const int y;
};

class Point3D : public Point
{
public:
    int z;
    Point3D(int a, int b , int c) : Point(a, b), z(c){
        std::cout << "자식" << std::endl;
    }
};
int main()
{
    Point3D p3d(1,2,3);
}
코드를 해석하자면,
1. Point3D p3d(1, 2, 3)
2. Point3D 호출
3. Point3D(int a, int b, int c) : Point(a, b), z(c)
    {
std::cout << "자식" << std::endl;
    } 

4. Point(a, b)  =>  Point를 a, b로 초기화

5. Point(int _x, int _y) : x(_x), y(_y)

    {

std::cout << "부모" << std::endl;

    }

    부모의 생성자 호출, 부모의 생성자 초기화완료 => 콘솔창에 "부모" 출력

6. Point3D(int a, int b, int c) : Point(a, b), z(c)

    {
std::cout << "자식" << std::endl;
    } 
    z를 c로 초기화
    자식의 생성자 초기화완료 => 콘솔창에 "자식"출력


이렇게 진행이되는데요

출력결과를 보겠습니다.



저도 처음에는 막연히 부모 -> 자식 순서로 된다고 알고있었는데

신기하네요


정리하자면


1. 멤버 이니셜라이저를 통해 const를 초기화할 수 있다.

2. 멤버 이니셜라이저는 변수 선언 순서대로 진행된다.

3. 생성자는 알고보면, 자식생성자에서 부모생성자를 호출하는것이다.



[알게된점]

1. 생성자 호출과정의 심화

2. 상수초기화


많이 쓰인다고 하는데 저는 아직 많이 안써봐서 익숙치가 않네요

차차 쓰면서 익혀둬야겠습니다.

잘못된 부분이 있다면 지적해주시면 감사하겠습니다!