생성자, 소멸자
요약
생성자(Constructor): 객체를 초기화 하는 멤버함수. 클래스 스스로 자신을 초기화하는 방법을 정의한다.
기본 생성자(Default Constructor): 인수를 가지지 않는 생성자
소멸자(Destructor): 객체가 소멸될 때 실행되는 멤버함수. 동적으로 할당된 객체의 메모리를 해제할 때 사용한다.
생성자
-
생성자(초기자, 구성자, Constructor, ctor) - 객체를 초기화 하는 멤버함수. 클래스 스스로 자신을 초기화하는 방법을 정의한다.
- 사용 이유
- 선언문이 초기화를 겸하면 코드가 간결해질 것
- C++은 클래스 객체에 대해 단순 타입에 적용되는 선언 및 초기화 문법을 제공하지 않음. 왜냐하면 멤버의 수와 타입이 가변적이기 때문.
- 이에 초기화를 전담하는 별도의 함수 필요
-
역할 - 객체 초기화
-
자동 호출 주체 - 컴파일러
- 형태 - “클래스이름”
#include <iostream>
using namespace std;
class unit
{
protected:
int hp;
int atk;
public:
unit()
{
cout << "기본생성자 호출" << '\n';
cout << "hp: " << hp << '\n';
cout << "atk: " << atk << "\n\n";
}
unit(int _hp, int _atk) :
hp(_hp), atk(_atk)
{
cout << "오버로딩 생성자 호출\n";
cout << "hp: " << hp << '\n';
cout << "atk: " << atk << "\n\n";
}
};
int main()
{
unit player;
unit monster(100, 10); // 암시적인 호출
}
기본생성자 호출
hp: -858993460
atk: -858993460
오버로딩 생성자 호출
hp: 100
atk: 10
위처럼 특징은
- 생성과 동시에 호출됨
- 오버로딩 가능
- 미초기화 시 쓰레기값으로 초기화
- 리턴값이 없음
기본 생성자
-
기본 생성자(디폴트 생성자, Default Constructor) - 인수를 가지지 않는 생성자
- 사용 이유
- 호출부에서 특정 값으로 초기화하고 싶지 않을 때
- 어떤 값인지 알지도 못하는 쓰레기값보다는 0 or -1 or NULL or “” 와 같이 초기화되지 않았음을 명시할 때
- 자동 호출:
- 클래스에 생성자가 정의되어 있지 않으면 컴파일러가 자동으로 디폴트 생성자를 만든다.
- 기본, 혹은 오버로딩한 생성자가 하나라도 있다면 컴파일러는 디폴트 생성자를 만들지 않는다.
- 객체의 초기화 방식 == 일반 변수와 같은 규칙 적용
- 전역 or 정적 객체 => 모든 멤버가 0으로 초기화
- 지역 객체 => 초기화되지 않은 쓰레기값
#include <iostream>
using namespace std;
class unit
{
protected:
char* name;
int hp;
int atk;
public:
unit(int _hp)
{
hp = _hp;
}
};
int main()
{
unit monster; // 생성자가 하나 이상 정의되어 있어 컴파일러가 기본 생성자를 자동 호출하지 않아 에러
}
// Error
default 키워드
기본 생성자는 컴파일러가 자동으로 호출하고 싶고, 별도 오버로딩한 생성자를 만들고 싶다?
default 키워드 - C++11 부터 명시적으로 default 생성자를 선언할 수 있다.
#include <iostream>
using namespace std;
class unit
{
protected:
int hp;
int atk;
public:
unit() = default; // default 키워드
public:
void PrintInfo()
{
cout << "hp: " << hp << '\n';
cout << "atk: " << atk << '\n';
}
};
int main()
{
unit monster;
monster.PrintInfo();
}
hp: -858993460
atk: -858993460
소멸자
-
소멸자(파괴자, Destructor, dtor) - 객체가 소멸될 때 실행되는 멤버함수. 동적으로 할당된 객체의 메모리를 해제할 때 사용한다.
-
형태 - “~클래스이름”
-
사용 이유 - 객체, 메모리는 항상 자신이 생성되기 전의 상태로 돌려놓아야 함
-
역할 - 동적 할당된 메모리 해제
-
자동 호출 주체 - 컴파일러
#include <iostream>
using namespace std;
class unit
{
protected:
char* name;
int hp;
int atk;
public:
unit() = default;
unit(const char* _name, int _hp, int _atk)
{
int length = strlen(_name);
name = new char[length + 1]; // 동적 할당
strcpy_s(name, length + 1, _name);
hp = _hp;
atk = _atk;
cout << "생성자 오버로딩\n";
cout << "name: " << name << '\n';
cout << "hp: " << hp << '\n';
cout << "atk: " << atk << "\n\n";
}
~unit()
{
delete[] name; // 해제
cout << "소멸자 호출\n";
cout << "name: " << name << '\n';
cout << "hp: " << hp << '\n';
cout << "atk: " << atk << "\n\n";
}
};
int main()
{
unit monster("슬라임", 100, 10);
}
생성자 오버로딩
name: 슬라임
hp: 100
atk: 10
소멸자 호출
name: 硼硼硼硼硼硼硼硼硼硼硼硼:FzZ?
hp: 100
atk: 10
위처럼 특징은
- 동적 할당한 멤버 변수를 해제할 때 사용
위 경우 파괴자가 정의되어 있지 않다면 메모리 누수 발생 !
생성자와 소멸자의 공통 특징
- 이름이 정해져 있다.
- 리턴값이 없다.
- 반드시 public 액세스 속성을 가져야 한다. (특수 목적 제외. ex. 싱글톤 패턴)
- friend가 될 수 없다. => 둘 다 클래스 내부의 함수이므로 friend 지정이 없어도 멤버를 마음대로 액세스 할 수 있다.
- static이 될 수 없다. => 초기화와 정리의 대상이 클래스가 아니라 개별 객체이므로.
- 둘 다 컴파일러가 호출하는 디폴트가 있다. 인수를 취하지 않고 아무런 동작을 하지 않는다.
생성자와 소멸자의 차이
- 생성자는 인수가 있지만 파괴자는 인수가 없다.
- 생성자는 가상 함수로 정의될 수 없지만 파괴자는 가상 함수로 선언될 수 있다.
- 아직 만들어지지도 않은 객체에 대해 다형적인 특징을 사용할 수 없다.
그 외..
- 멤버 변수 초기화, 이니셜라이저 리스트는 초기화 파트에서 정리
- 상속 시 생성자, 소멸자 호출 순서는 상속 파트에서 정리
- virtual 소멸자는 가상 함수 파트에서 정리
- 복사 생성자, 복사 대입 연산자, 이동 생성자, 이동 대입 연산자는 별도 파트에서 정리
댓글남기기