프로그래머/CPP_강의정리

cpp 객체지향(6-초기화 리스트)

미역국마싯 2021. 9. 20. 10:11

멤버 변수 초기화

초기화 되지 않은 멤버 변수에는 컴파일이 쓰레기 값을 저장한다. 이는 버그를 발생시키는 주요 원인이다. 

따라서 멤버 변수는 초기화를 해야 버그가 발생하지 않는다. 이제 초기화 방법을 알아보자.

 

 

초기화 방법

생성자 내에서 설정

class Knight
{
public:
	Knight(int hp)
	{
		_hp = hp;
	}


public:
	int _hp;
};

 

초기화 리스트

초기화 리스트는 상속 관계에서 원하는 부모 생성자를 호출할 때 필요하거나, 포함 관계에서 원하는 클래스의 생성자를 호출할 때 필요하다.

// Knight와 포함 관계에 있는 클래스
class Inventory
{
public:
	Inventory() { cout << "Inventory()" << endl; }
	Inventory(int size) { cout << "Inventory(size)" << endl; }

	~Inventory() { cout << "~Inventory()" << endl; }

public:
	int _size = 10;
};

// 부모 클래스
class Player
{
public:
	Player() { }
	Player(int id) { }

};

Knight는 Player를 상속받고 Inventory를 가지고 있다.

class Knight : public Player
{
public:
	Knight() : Player(1), _inventory(20)	// -> 초기화 리스트
	{
		/*
		선처리 영역
		초기화 리스트를 이용해서 
		Player(id) Inventory(size) 생성자를 실행
		*/
	}


public:
	int _hp;
	Inventory _inventory;
};

초기화 리스트를 이용해서 선처리 영역에서 부모 클래스와 포함 클래스의 생성자를 호출하여 특정 값을 넣어준다.

또한 포함 클래스는 멤버 변수에 넣어서 포함 관계를 직관적으로 알 수 있도록 한다.

 

여기서 알아야 할 점은 Knight가 생성됐을 때, 일반 멤버 변수와 달리 클래스 멤버 변수인 Inventory도 전처리 영역에서 생성된다는 점이다. 그렇다. 부모 클래스가 전처리 영역에서 생성된 것처럼 클래스 멤버 변수도 전처리 영역에서 생성된다!

 

 

만약 초기화 리스트를 이용하지 않고 생성자 내에서 클래스 멤버 변수를 초기화시키면 어떻게 될까?

class Knight : public Player
{
public:
	Knight()
	{
		/*
		선처리 영역
		Inventory() 실행
		*/
		_inventory = Inventory(20);
	}

 

 

Knight(10, 100)을 실행하면 먼저 Inventory의 기본 생성자가 실행된다. 하지만 그 후에 실행된 Knight 생성자에서 Inventory의 size를 정해준 생성자를 실행시킨다.

이때 새로운 inventory의 생성자가 실행됨과 동시에 기존에 있던 inventory 기본 생성자는 없애야 한다.

따라서 실행 순서는 [ Knight() -> Inventory() -> Inventory(size) -> ~Inventory() ... ] 처럼 진행된다.

이는 비효율적인 처리 과정을 거치기 때문에 지양해야 한다.

 

 

마지막으로 초기화 리스트는 정의함과 동시에 초기화가 필요한 경우에도 이용한다.

대표적인 예로 참조와 const가 있다.

class Knight
{
public:
	Knight() :  _hpRef(_hp), _hpConst(100)
	{
		_hpRef = _hp;
		_hpConst = 100;
	}


public:
	int _hp;
    
	int& _hpRef;	
	const int _hpConst;
};

참조는 어떤 주소를 가지고 있어야 한다. const는 읽기만 할 수 있다. 즉, 둘 다 선언과 동시에 초기화가 필요하다.

따라서 위처럼 Knight 생성자를 실행해서 초기화 시키면 되지 않을까? 하겠지만 의미없는 방식이다. 

_hpRef는 _hp의 주소를 가리키고 있지 않고, _hpConst는 100으로 초기화되어 있지 않다.

 

class Knight
{
public:
	Knight() :  _hpRef(_hp), _hpConst(100)
	{
		/*
		선처리 영역에서 참조와 const 값을 초기화 한다.
		*/

	}

생성자가 실행하자마자 참조와 const는 수정할 수 없다. 이는 선처리 영역에서 이미 초기화했기 때문이다.

선처리 영역은 Knight 생성자가 실행되기 전에 미리 세팅되는 영역이다.

따라서 원하는 값을 초기화하기 위해서는 초기화 리스트를 이용해서 참조와 const에 미리 값을 지정해야 한다.

 

 

cpp11 문법

최근에 업데이트된 cpp 문법인데 살짝 알아보자.

class Knight : public Player
{
public:
	Knight() 
	{

	}


public:
	int _hp = 100;

	int& _hpRef = _hp;
	const int _hpConst = 100;
};

이렇게 멤버 변수 쪽에서 바로 초기화할 수 있다.

 

 

 

출처

https://www.inflearn.com/course/%EC%96%B8%EB%A6%AC%EC%96%BC-3d-mmorpg-1/dashboard

 

[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part1: C++ 프로그래밍 입문 - 인프런 | 강의

시리즈를 원활하게 학습하기 위한 기초적인 C++ 문법들에 대해 학습합니다. 어셈블리 언어부터 시작해서 기본 C++ 문법, STL, C++11까지 핵심적인 내용을 압축해서 다루게 됩니다., - 강의 소개 | 인

www.inflearn.com