본문 바로가기
IT/C++(CPP)

CPP 초급 강좌 29. 맴버 초기화 리스트 member initializer lists

by 신림83 2020. 11. 10.
반응형

CPP 초급 강좌 29. 맴버 초기화 리스트 member initializer lists

클래스의 맴버를 초기화하는 코드를 알아봅시다.

다음 코드를 볼까요.

class Point {
private:
	int x, y;

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

int main()
{
	Point p(1, 2);
}

 

Point란 클래스를 만들고 인자로 x, y가 있습니다. 이 값은 main에서 객체를 생성할 때, p(1, 2); 코드로 인자 2개의 생성자를 호출하며 내부 값을 초기화시키죠.

 하지만 위의 행위의 정확한 표현은 초기화가 아닙니다. 대입입니다.

생성자 호출 시 클래스 멤버에 대한 초기화 문법이 따로 존재합니다. 아래를 봅시다.

 

Point(int a, int b) : x(a), y(b)
{ 
	//x = a; y = b; 
}

 

이와 같이 생성자 뒤로, 맴버에 대한 초기화를 하는 문법이 존재합니다. 이를 맴버 초기화 리스트라고 말합니다.

 

이건 초기화고, 위에 거는 대입이라는데, 무슨 소리인가요?라고 생각하실 수 있습니다.

대입과, 초기화의 차이에 대해 코드를 봅시다.

int n1;     
n1 = 0;	    //a

int n2 = 0; //b

 

a 는 대입입니다. n1이 생성, 초기화된 이후 값을 대입합니다.

b 는 초기화입니다. 생성하며 값이 정해집니다.

 

대입과 초기화는 분명 다른 영역입니다.

 int로 설명하면 와 닿지 않을 수 있는데, 만약 객체라 생각해보면 많은 차이를 가지게 됩니다. 당장 생성자 호출 차이부터 존재하게 됩니다.

 

광고과고

꼭 맴버 초기화 리스트를 사용해야 되는 경우

 특정 맴버에 값을 채우러 할 때, 일반적인 인자 전달방식이 아닌, 꼭 맴버 초기화 리스트 기능을 사용해야 되는 경우가 있습니다. 여기에 관하여 대하여 봅시다.

 

const, reference

class Point {
private:
	int x, y;

	const int v1;
	int& v2;

public:
	Point(int a, int b, int c, int d) : x(a), y(b)
	{ 
		v1 = c;
		v2 = d;
	}
};

int main()
{
	Point p(1, 2, 3, 4);
}

 

 가능한 코드일까요?

const int v1에 값이 대입이 가능할까요? int& v2인 reference 에 값이 대입이 가능할까요?

둘 모두 초기화로만 값을 넣을 수 있는 아이들입니다. 이와 같은 멤버 변수는 꼭 맴버 초기화 리스트를 사용하셔야 합니다.

 

Point(int a, int b, int c, int d) : x(a), y(b), v1(c), v2(d)

이렇게 수정하셔야만 빌드, 진행이 가능하십니다.

 

디폴트 생성자가 없는 객체가 멤버 데이터 일 경우

class Point {
private:
	int x, y;

public:
	Point(int a, int b) : x(a), y(b) 
	{ }
};

class Triangle
{
private:
	Point p1;
	Point p2;
	Point p3;

public:
	Triangle() { cout << "Triangle()" << endl; }
};

 

Triangle 은 정상적으로 빌드가 될까요? 빌드되지 않습니다. Point는 기본 디폴트 생성자가 없기 때문에 컴파일러에서 오류를 냅니다.

오류결과

Triangle의 생성자는 정상 동작하지 않습니다. Point 멤버변수들이 정상적인 생성이 되지 않기 때문입니다. 이렇게 맴버객체가 디폴트 생성자가 없는 경우에도, 맴버 초기화 리스트를 사용하여 대응해야 합니다.

 

Triangle() : p1(0,0), p2(0, 0), p3(0, 0) {}

이와 같이 수정해야 정상 동작이 가능합니다.

 

선언, 구현을 나눌 때

선언

Triangle();

 

구현

Triangle::Triangle() : p1(0, 0), p2(0, 0), p3(0, 0) {}

 

맴버 초기화 리스트는 구현부에 들어가야 합니다.

 

맴버 리스트 초기화시 맴버 리스트를 이용하려 할 때 주의사항

class Point {
private:
	int x, y;

public:
	Point(int a, int b) : y(a), x(y) 
	{ 
		cout << x << endl;
	}
};

int main()
{
	Point p(1, 2);
}

위의 코드를 보세요. x 값은 무엇이라 출력될까요 y에 a의 값이 들어가고 x에 y의 값이 들어가니까 1이라고 생각하시겠죠?

안타까운 출력결과

 정답은 정의되지 않은 값이 출력됩니다. 멤버 초기화 리스트의 경우 오른쪽에 나열된 결과가 아닌 멤버가 선언된 순서에 따라 초기화가 됩니다. int x, y로 선언되어 있어 x가 우선순위가 높아 먼저 초기화되어 y의 초기화되지 않은 값이 x의 값이 된 것입니다. int y, x; 로 선언하시면 기대한 동작을 하는 것을 보실 수 있어요.

 

이와 같은 특성이 있는 걸 아시면 좋습니다.

 

미묘한 것이 많습니다.. 공부합시다.

봐주셔서 감사합니다.

반응형

댓글