C++복습

C++복습) 생성자 맴버 초기화 리스트

PJNull 2021. 11. 22.
728x90
반응형

 

맴버 변수의 초기화는 다양한 문법이 존재한다.

그런데 번거롭게 맴버 변수의 초기화는 왜 이루어 져야되는것인가?

class Mage
{
public:
 Mage(){}
public:
 int _hp;
};

int main()
{
 Mage m;
 cout<<m._hp;//초기화 되지 않아서 쓰레기값을 들고 있음
}

즉, 초기화를 하는 이유는 다른 프로그램에 의해 사용되어진 메모리안에 사용했던 값이나 예기치 못한 쓰레기 값을 들고 있어서 포인터 및 주소값과 연루되어 있을 경우 심각한 에러나 버그를 동만할수 있기에 초기화를 해야만 한다.

 

초기화 방법으로는 생성자내에서 하는 방법과 초기화리스트, 그리고 C++11문법등이 있다.

 

 

 

 

생성자내에서의 처리

 

이전에 작성했던 생성자/소멸자에서 부모생성자의 초기화와 동일한 문법을 가진다.

class Player
{
 Player(){}
 Player(int id){}
 
};
class Mage: public Player
{
public:
 Mage():Player(1),_hp(100)
 //생성자내에서 부모클래스의 생성자를 초기화함과 동시에 맴버변수 _hp를 100으로 초기화
 {}
 public:
 int _hp;
};

 

 

 

 

초기화 리스트

 

일반 변수의 경우 생성자내에서의 처리와 차이가 미미하다. 다만, 맴버타입이 클래스인 경우 차이가 분명하다.

맴버타입의 클래스 구분은 Is-A관계Has-A관계를 잘 고려해야된다.

Mage Is A Player?Mage Has An Inventory? 등의 관계가 성립하는지를 유심히 살펴보자.

만약  Is-A관계가 성립된다면 상속관계가 성립한다는 뜻이고,

Has-A관계가 성립된다면 포함관계가 성립한다는 뜻이 된다.

메이지는 플레이어이기에 Is-A관계가 성립되며 상속관계이다. 

메이지는 인벤토리를 가지고 있기에 Has-A관계가 성립되어 포함관계이다.

class Inventory
{
 public:
 Inventory(){cout<<"1";}
 Inventory(int size){cout<<"2";}
 ~Inventory(){cout<<"3";}
 
 public:
  int _size=10;
};


class Player
{
 Player(){}
 Player(int id){}
 
};
class Mage: public Player
{
public:
 Mage():Player(1),_hp(100)
 //생성자내에서 부모클래스의 생성자를 초기화함과 동시에 맴버변수 _hp를 100으로 초기화
 {
  _hp=100;//생성자내에서 초기화 한것과 동일하게 100으로 초기화됨
 }
 
 public:
 int _hp;
 Inventory _inventory;
};

맴버변수의 경우 생성자를 통한 초기화와 블록안에서의 초기화가 동일하다. 

다만, 이러한 포함관계에서는 해당 클래스의 생성자의 선처리 영역에서 포함관계에 있는 클래스의 생성자를 호출하게 된다.

class Mage: public Player
{
public:
 Mage():Player(1),_hp(100)
 /*
 선처리 영역
 */
 {
  _inventory=Inventory(20);
 }
 public:
 int _hp;
 Inventory _inventory;
};

결과)1,2,3,3

위와같이 해당 클래스 안에서 맴버타입 클래스를 초기화 하려고 할경우 선처리영역에서의  _inventory=Inventory();와

블록 안의  _inventory=Inventory(20);이 중복되어지며  _inventory=Inventory(20);에 의해  _inventory=Inventory();는 불필요해졌기에 소멸자가 호출이 되어지는 현상이 일어난다. 

class Mage: public Player
{
public:
 Mage():Player(1),_hp(100),_inventory(20)
 /*
 선처리 영역
 */
 {
 }
 public:
 int _hp;
 Inventory _inventory;
};

즉, 이러한 현상을 피하기 위해서는 내부에서 처리하기 보다는 초기화리스트를 이용하는 것이 중복을 피할수 있다.

또한 정의함과 동시에 초기화가 필요한 참조타입이나 const타입의 경우 반드시 초기화리스트를 이용해야 한다.

 

참조타입과 const타입

 

포인터인 경우 nullptr처럼 아무런 값을 가지고 있지않아도 되지만 참조타입의 경우 생성되었을때 반드시 어떠한 값을 가져야한다. 콘스트의 경우 생성되면 변경이 불가하다.

즉, 생성과 동시에 초기화 해주는 초기화리스트를 사용해야한다. 

class Mage: public Player
{
public:
 Mage():Player(1),_hp(100),_inventory(20),_hpRef(_hp),_hpconst(100)
 /*
 선처리 영역
 */
 {
 }
 public:
 int _hp;
 Inventory _inventory;
 int& _hpRef;
 const int _hpconst;
};

 

728x90
반응형

'C++복습' 카테고리의 다른 글

C++복습) Modern C++/ Nullptr  (0) 2021.12.22
C++복습) Modern C++/ Auto  (0) 2021.12.22
C++복습) 다형성  (0) 2021.11.21
C++복습) 클래스 캡슐화(은닉성)  (0) 2021.11.20
C++복습) 클래스 상속성  (0) 2021.11.20

댓글