TIL 2024/8/05

Sung Joo Lee·2024년 8월 5일

생성자


  • 특수한 멤버 함수

  • 객체가 생성될 때 자동으로 호출됨

  • 초기화 목적으로 유용하게 사용됨

  • 클래스와 동일한 이름을 갖는 멤버 함수

  • 반환형은 존재하지 않음

  • 오버로딩 가능

class Player
{
public:
	Player();//생성자
	Player(std::string name);
	Player(std::string name, int health, int xp);
private:
	std::string name;
	int health;
	int xp;
};

생성자: 반환형이 없고 , 함수의 이름이 클래스의 이름과 동일.

기본 생성자


  • 인자가 없는 생성자

  • 클래스의 생성자를 직접 구현하지 않으면 컴파일러가 기본적으로 만들어 줌

    • 초반부에, 생성자가 없는 상태에서도 객체를 만들 수 있었다.
    • 컴파일러가 기본 생성자를 알아서 만들어서 사용했기 때문
  • 객체를 인자 없이 생성하면 호출된다.

  • 객체를 생성 할 때 멤버 변수들의 값이 쓰레기 값이 들어가지 않게 초기화 시켜줄 수 있다

  • *주의*

    • 생성자를 하나 지정해 주었으면 다른 모든 생성자들 또한 지정해 줘야함
class Player {

	Player()// 객체가 생성될 때 어떤 필드 값을 가지고 생성될지 지정해주는 것
	{
	   health = 100;
	   xp = 100;
	   name = "mkae";
	}

};
#include <iostream>

class Player {

	/*
	Player()
	{
	   컴파일 하면 자동으로 기본 생성자가 생김
	}
	*/
};

int main()
{

	Player kim;
	Player* enemy = new Player;

	return 0;
}
  • 위의 클래스는 아무 것도 작성하지 않은 코드이지만 컴파일 하면 기본 생성자가 생긴다.

직접 확인


#include <iostream>

class Account
{
public:

	Account()//객체가 생성될 때 호출된다.
	{
		std::cout << "계좌 생성됨" << std::endl;
	}

	~Account()
	{
		std::cout << "소멸자 호출됨" << std::endl;
	}

	void Withdraw(double amount) //출금
	{
		if (balance - amount < 0)
			return;

		balance -= amount;
	}

	void Deposit(double amount) //입금
	{
		balance += amount;
	}
private:

	std::string name;
	double balance; //계좌 잔액
};

int main()
{
	//객체의 생성
	Account kimAccount;

	kimAccount.Withdraw(100);

	return 0;

}
  • 이전에 우리가 작성했던 코드를 보고 한번 확인 해보자.
  • 결과

위의 결과를 보면 Account가 생성될 때, Account가 파괴될 때 호출이 되는 것을 직접 확인 할 수 있다.

생성자 오버로딩


  • 오버로딩을 통해 객체가 생성 될 때 기본 값을 가지게 할지, 필드의 값을 지정해 줄 수 있다

  • 매개변수에 따라 호출되는 생성자가 다르다.

  • 각각의 생성자는 고유해야 함(매개변수가 달라야 함)

    • 함수 오버로딩과 동일한 규칙
	Account()
	{
		balance = 0.0;
		name = "None";
	}

	Account(double val, std::string str)
	{
		balance = val;
		name = str;
	}
	
	
	/*-----------중략--------- */
	
	int main(){
	
		Account Kim_account;//이때의 balance = 0.0, name = None 으로 생성
		Account Lee_account = Account{100.0,"Lee"};
		
	}
	

주의


  • 인자가 있는 생성자만 구현한 경우에는 “기본 생성자”가 자동 생성되지 않음

	Account(double val, std::string str)
	{
		balance = val;
		name = str;
	}
	
	
	/*-----------중략--------- */
	
	int main(){
	
		Account Kim_account;// ERR
		Account* LeeAccount = new Account;//ERR
		delete LeeAccount;
		
		Account* ParkAccount = new Account {"park",1000.0}; // OK!
		delete ParkAccount;
		
	}
	

위와 같이 코드가 구현이 되어있다면 지정한 생성자 이외의 기본 생성자는 자동 생성되지 않는다!

Why?

  • 사용자가 일부러 의도하여 매개변수 2개를 받는 객체만 만들고 싶을 수 있기 때문에 의도적을 방지해 준 것.

생성자 초기화 리스트


  • 진짜 초기화 방법
    • 생성자가 선언되고 필드의 값이 대입이 될 때까지 해당 필드의 값은 쓰레기 값이다.

    • 이를 방지하기 위해서 사용한다.

      	Account(double val, std::string str)
      	{
      		balance = val;
      		name = str;
      	}

      위와 같이 생성자를 작성했을 때 balance와 name의 값은 val과 str이 초기화 되기 전까지 여전히 쓰레기 값을 가지고 있다. 여기서는 문제가 크게 없지만 만약 아래와 같은 코드가 있다면

      	Account(double val, std::string str)
      	{
      		balance *= 2;
      	
      		balance = val;
      		name = str;
      	}

      위의 코드 balance의 값을 쓰레기 값의 2배를 해준다. 이와 같은 경우를 방지해 주는 것이다.

  • 생성자 초기화 리스트를 사용할 경우 생성과 동시에 값이 지정된다.
Account :: Account(double val, std::string str) // 전방선언 후 main뒤에서 함수 작성
					: name{str}, balance {val}
{
}

Player::Player()
				: name{"None"},health{100},xp{50}
{
}

즉, 위와 같이 작성해야 객체가 생성 되자마자 필드의 값이 초기화가 된다.

생성자 위임


  • 다양한 생성자의 오버로딩에 유사한 코드가 반복적으로 사용

  • 오류의 가능성이 높아짐

  • 생성자 위임을 통해 오류 가능성과 코드 반복을 줄일 수 있음

    • 다른 생성자를 멤버 초기화 리스트 위치에서 호출
    • 생성자 멤버 초기화 리스트를 사용해서만 가능

예시


생성자 위임 사용 X

Account :: Account(double val, std::string str) // 전방선언 후 main뒤에서 함수 작성
					: name{str}, balance {val}
{
}

Player::Player()
				: name{"None"},health{100},xp{50}
{
}

생성자 위임 사용

Player::Player(std::string name_val,int health_val, int xp_val)
		:name{name_val},health{health_val},xp{xp_val}
{
}

Player::Player()
		: Player{"None",0,0}//Player 생성자 호출한 이후 인자를 넘겨주고있음
{
}

Player::Player(std::string name_val)
		: Player{name_val,0,0}//Player 생성자 호출
{
}
  • 위와 같이 생성자 위임을 사용하여 같은 코드를 반복 할 필요없이 간단하게 객체 생성시 값을 초기화 시킬 수 있음

생성자 기본 매개변수

  • 생성자 또한 함수이므로, 기본 매개변수 사용 가능
#include <iostream>

class Player
{

public:
	Player(std::string nameval = "None", int healthVal = 0, int xpVal = 0);//프로토타입
private:
	std::string name;
	int health;
	int xp;

};

int main()
{

	Player empty;
	Player Kim("Kim");//kim,0,0
	Player hero("Hero", 100, 0);
	Player enemy("Enemy", 1000, 0);

	return 0;
}

Player::Player(std::string nameVal, int healthVal, int xpVal)
	:name{ nameVal }, health{ healthVal }, xp{ xpVal }
{
}
  • 우리가 이전에 공부했던 함수의 기본 매개변수와 동일하게 생각하면 된다.

소멸자


  • 특수한 멤버 함수

  • 객체가 소멸할 때 자동으로 호출됨

    • 스택과 힙에서 해제될 때
  • 메모리 및 기타 리소스 ( 파일 close등)해제 목적으로 유용하게 사용됨

  • 클래스와 동일한 이름 앞에 “~”를 갖는 멤버 함수

  • 반환형 및 매개변수는 존재하지 않음

  • 오버로딩 불가능

class Player
{
public:
	Player();//생성자
	Player(std::string name);
	Player(std::string name, int health, int xp);
	~Player(); //소멸자
private:
	std::string name;
	int health;
	int xp;
};

소멸자 호출 시점

객체 또는 객체의 포인터가 소멸되는 시점에서 자동으로 호출 된다

int main()
{
	Player slayer;
	Player Kim{ "kim",100,4 };
	Player hero{ "Hero" };
	Player enemy{ "Enemy" };
	/*여기서 파괴자 호출 됨*/
	return 0;
}
Player* enemy = new Player{"Enemy2",1000,0};
delet enemy;// 파괴자 호출 시점
  • 위와 같이 소멸 시점에서 자동으로 호출된다.

요약

  • 생성자
    • 객체가 생성될 때 자동 호출, 반환 없음
  • 소멸자
    • 객체가 소멸될 때 자동 호출, 오버로딩 불가능, 반환 없음
  • 기본 생성자
    • 명시하지 않으면 자동으로 만들어주지만, 쓰레기 값은 없어야 하므로 구현 필요
    • 인자가 있는 생성자를 만들면 기본 생성자는 자동 생성되지 않는다.
profile
개발로그

0개의 댓글