생성자와 소멸자는 클래스가 언제 생성되고 언제 소멸되는지를 보여준다.
스택이냐 힙이냐 종류가 다르면 소멸하는 시기가 다르다.
이 포스팅에서는 스택과 힙에 객체를 만들면 어떤 차이가 생기는지 알아본다.
자바와 달리 C++ 에서는 스택에 객체를 만들 수 있다. new 키워드를 사용하지 않고 객체를 선언하면 된다.
아래의 클래스로 테스트 한다.
생성자는 오버로드 하는데 매개변수가 없는 MyDog 은 스택의 생성에 사용한다.
매개변수가 있는 MyDog 은 힙에 만들 객체에 사용한다.
MyDog WangWang 을 선언하는 순간에 생성자가 호출된다.
MyDog* ptrDoolly 로 new 키워드로 힙영역에 객체가 생성된다.
다음 delete 로 힙영역에서 소멸시킨다.
결과를 놓고 보면 스택의 소멸자는 더 나중에 호출된다. 이것은 main 함수가 종료하기 전에 지역변수들을 메모리에서 해제하면서 호출된다. 힙영역에 생성한 객체는 뒤에 선언되었음에도 불구하고 delete 명령어로 이미 소멸이 된 상태다.
만약 delete 를 호출하지 않는다면 아래와 같은 결과를 볼 수 있다. 힙영역의 소멸자는 호출하지 않는다. 이것을 보면 스택의 객체는 main 함수가 종료될 때 소멸을 시키는데 힙영역의 객체는 그 과정을 건너뛰어 버린다. 그냥 프로그램이 종료하면서 함께 사라져버린다. 물론 메모리는 반환되었겠지만 명시적으로 delete 를 사용하는 것이 좋은 방법이다. 메모리가 통째로 버려지는 기분이라 찜찜하다.
스택에 만든 지역변수 객체는 . 도트연산자로 객체의 멤버에 접근할 수 있다. 한편 힙에 만든 객체는 포인터로 접근해야한다. 간접연산자 * 를 사용하는 C++ 문법에서는 (*포인터).멤버 로 해야한다. 그런데 이게 번거로우니까 포인터->멤버 이 표현으로 대체해버렸다. 갑자기 -> 이게 나오면 헷갈릴 수 있는데 (*포인터). 를 줄인 것이다.
객체 변수 자체에는 클래스에 대한 특별한 참조를 가지고 있는데 이는 메모리 주소값과는 다른 것이다. 클래스를 선언한 변수 위에서는 WangWang 도 메모리 주소를 가지고 있지만 그 안에 들어있는 내용은 객체를 참조할 수 있는 정보를 가지고 있다.
테스트를 하기 위해 public 에 위와 같은 메소드를 만들었다.
위에서 (*ptrDoolly).bark는 -> 이것과 같다.
비주얼 스튜디오에서는 자동완성을 지원한다. 도트연산자 . 를 커밋하면 -> 로 바꿔주는 편리한 기능이 있다.
생성자와 소멸자를 사용해서 스택과 힙에서 객체의 생성과 소멸이 다르다는 것을 알 수 있었다.
C++에서는 delete를 명시적으로 사용하는 습관이 필요하다. 프로그램이 종료되면 힙메모리도 시스템에 반환되지만 그렇게 놔두는 것은 C++에서 좋은 방법이 아니다.
포인터가 사용하는 -> 연산자는 실제는 (*포인터).멤버 로 변환된다.
*소스코드
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
#define Line cout <<"\n-----------------------------\n"
class MyDog
{
public:
MyDog();
MyDog(string name);
~MyDog();
void bark();
private:
string name = "기본";
int age;
};
MyDog::MyDog()
{
cout << " 생성자 호출 : 기본" << endl;
}
MyDog::MyDog(string name)
{
this->name = name;
cout << " 생성자 호출 : " << this->name << endl;
age = 2;
}
MyDog::~MyDog()
{
cout << " 소멸자 호출 : " << this->name << endl;
}
void MyDog::bark()
{
cout << "Wang! Wang! ";
cout << "I am " << this->name << endl;
}
int main()
{
MyDog WangWang = MyDog("왕왕이");
MyDog* ptrDoolly = new MyDog(" 둘리");
Line;
WangWang.bark();
ptrDoolly->bark();
Line;
cout << " 힙 메모리를 해제합니다\n";
delete ptrDoolly;
Line;
return 0;
}