생성자와 소멸자의 개념
- 생성자와 소멸자는 멤버함수입니다. 하지만, 사용자가 꼭 정의하지 않아도 됩니다.
- 정의하지 않는 것이라고 해서 존재하지 않는 것은 아니며 시스템 내부에서 객체의 생성, 소멸을 담당합니다.
- 사용자가 이를 조금 더 유용하게 사용하고자 하는 경우, 가시화시켜 클래스 내부에 선언하고 정의할 수 있습니다.
생성자란?
int num1 = 500;
의 코드는 무엇을 의미할까요, 모두들 아시겠지만 이것은 메모리에 int형 기억장소 4바이트를 메모리에 할당한 다음, 그 곳에 500이라는 초깃값을 주는 것입니다. 이처럼 char, float 등의 일반 자료형들의 경우 컴파일러가 알아서 그 동작을 수행합니다.
하지만 클래스의 경우에는 그것을 사용자가 정의합니다. 그러한 경우에는 어떻게 객체를 초기화할까요? 바로 생성자가 그 역할을 담당합니다. 생성자는 사용자가 특별히 지정하지 않아도 자동으로 호출이 되지만, 멤버에 대한 초기화 자료를 지정하지 않으면 가비지 값을 가지게 됩니다. 생성자는 멤버변수의 초기화를 담당하는 역할을 합니다.
#include <iostream>
using std::cout;
class Dog {
private:
int age = 13;
//int age; 로 끝내면..
public:
int getAge();
void setAge(int a);
};
int Dog::getAge()
{return age;}
void Dog::setAge(int a)
{age = a; }
int main()
{
Dog happy,coco;
cout << "happy나이 : " <<happy.getAge()<<"\n"
<< "coco나이 : " <<coco.getAge();
return 0;
}
//happy나이 : 0
//coco나이 : -117735264
//초깃값을 제공하지 않으면, 가비지 값을 가집니다.
생성자의 예시 코드입니다. 이 경우에서 멤버에 대한 초기화 값을 제공하지 않으면, 가비지 값이 나올 것입니다.
생성자는 다음의 특징을 가집니다.
- 생성자의 이름은 클래스명과 같습니다.
- 클래스의 객체가 생성될 때마다, 자동으로 호출됩니다. – 사용자가 호출할 수 없습니다.
- 객체가 메모리에 할당될 때 멤버 변수의 초기화를 담당합니다.
- 리턴형을 쓰지 않습니다.
- 하나의 클래스에 여러 개의 생성자가 존재할 수도 있습니다.
- 생성자가 반드시 있어야 하는 것은 아니지만, 메모리를 초기화한다는 의미에서 있는 것이 좋습니다.
#include <iostream>
using std::cout;
class Fish{
private:
float length;
public:
Fish(){length = 24.3;} // 생성자를 정의했습니다.
float getLength(){return length;}
void setLength(float a){length = a;}
};
int main()
{
Fish discus;
cout<<discus.getLength();
return 0;
}
이러한 식으로 생성자를 정의해서, private 멤버변수인 length의 초깃값을 넣을 수 있습니다.
소멸자란?
- 소멸자는 클래스의 객체가 소멸될 때 자동으로 호출됩니다.
- 이름은 클래스명과 같으며 앞에 “~”를 붙이면 됩니다.
- 하나의 클래스에 하나만 존재합니다.
- 리턴형과 매개변수가 없습니다.
- 생성자와 마찬가지로, 사용자가 직접 호출할 수 없습니다.
- 객체가 소멸될 때 자동으로 호출되는 특징이 있기에, 객체가 소멸될 때 동작했으면 하는 코드를 삽입합니다.
- 소멸자는 사용한 메모리 공간이 더 이상 불필요하게 될 때, 해당 메모리를 시스템이나 다른 객체에 반납하는 용도로 많이 사용합니다.
#include <iostream>
using std::cout;
class Fish{
private:
int length;
public:
Fish(int a){length = a; cout<<"뻐끔뻐끔...\n";}
//생성자를 정의했습니다.
~Fish(){cout<<"소멸되었음!!!\n";}
//소멸자를 정의했습니다.
int getLength();
void setLength(int a);
};
int Fish::getLength()
{
return length;
}
void Fish::setLength(int a)
{
length = a;
}
int main()
{
Fish guppy(5); // 초기화
cout<<"메인함수가 시작됩니다...\n";
cout<<guppy.getLength();
cout<<"\n메인함수가 끝났습니다...\n";
return 0;
}
//뻐끔뻐끔...
//메인함수가 시작됩니다...
//5
//메인함수가 끝났습니다...
//소멸되었음!!!
코드를 보고, 결과가 왜 그렇게 나오는지 이해해 봅시다. 생성자를 “뻐끔뻐끔”을 출력하도록 하였기 때문에 fish guppy(5)로 객체가 생성되는 순간 “뻐끔뻐끔”이 가장 먼저 출력되는 것을 볼 수 있습니다.
다음으로 메인함수가 시작되고, getLength()함수로 guppy의 length값을 얻어왔습니다. 메인함수가 끝난 후, 객체가 소멸될 때에 소멸자가 호출될 것이고, 정의했던 것처럼 “소멸되었음!!!”이 출력되는 것을 확인할 수 있습니다.
이때 위의 코드에서 fish guppy(5) 인데 왜 length가 5로 지정되는 것일까요?
#include <iostream>
using std::cout;
class Dog {
private:
int age;
public:
Dog(int a) {age = a;}
//생성자의 매개변수로 멤버 변수를 초기화하였습니다.
int getAge() { return age; }
void setAge(int a) { age = a; }
};
int main()
{
Dog coco(3); // age = 3; 을 수행합니다.
cout<<coco.getAge(); // 3이 나올 것입니다.
cout<<"\n";
coco.setAge(21); // age = 21; 을 수행합니다.
cout<<coco.getAge(); // 21이 나올 것입니다.
return 0;
}
//3
//21
생성자의 매개변수로 멤버 변수인 age를 초기화하였기 때문에 그렇습니다. 위의 코드의 결과가 왜 3과 21이 나오는지 이해하실 수 있을 것이라 생각합니다.