다형성을 가진 기본클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자.

출처 : Effective C++. 항목 7 : 다형성을 가진 기본클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자.

상속을 사용한 객체를 할당하고 해제할때 기반클래스의 포인터를 사용하여 할당 및 해제를 사용하는 경우가 무척 많다. 이 경우 기반 클래스 및 자식 클래스의 소멸자가 virtual 이 붙어야 하는 경우와 붙지 말아야 하는 경우가 있다.
class Base {
// …
};

class Derived : public Base {
// …
};

Base* object = new Derived();
// object 사용
delete object;

위와 같은 코드가 있을 경우 Base 의 포인터를 이용해 할당을 하고 Base형의 포인터를 이용해 메모리를 해제한다.
이 경우의 문제는 Base의 소멸자가 virtual 이 아니라면 소멸되는 과정에서 상속받은 클래스의 소멸자가 정상적으로 호출되지 않게 되서 정의되지 않는 동작이 일어나게 된다. 즉 기반클래스의 소멸자는 virtual로 선언 되어야 한다. 뒤집어 이야기 한다면 virtual 로 선언되지 않은 소멸자를 같는 클래스는 기반클래스(일반적인 의미의 인터페이스를 제공하는)로 사용되 않을 확률이 높다.

예를 들어
class Point{
public:
Point(int x, int y);
~Point();
private:
int x, y;
};
은 딱 64비트 크기에 맞는 크기다. 이 경우 64비트 레지스터에 쏙 들어감으로써 최적화된 코드라고 볼수도 있다. 하지만 이 Point 클래스의 소멸자를 virtual 로 선언하는 순간 각 Point 클래스의 객체는 버추얼테이블 포인터를 가져야 하며 64비트 크기를 넘어서게 된다. 즉 이런 경우에는 가상소멸자는 어울리지 않는다.

다시 한번 강조하면 클래스가 가상함수를 하나 이상 가지고 있는 경우에만 소멸자를 virtual로 선언한다 를 기억해 둬야 한다.

그런데 많은 실수중에 하나가 C++ , STL 에서 제공하는 표준 클래스들을 상속받는 경우다.
class GoodString: public std::string{
};
// …
GoodString *pss = new GoodString(“어쩌구저쩌구”);
std::string* ps = pss;
delete ps; // GoodString의 소멸자가 호출되지 않음. 에러발생.
std::string, std::vector 등의 클래스들은 virtual 소멸자를 갖지 않으므로 이들을 상속받아 추가적인 내용을 구현하는 경우 기반클래스의 포인터로 할당하고 해제할 경우 정의되지 않는 동작이 일어나므로 이런식으로 사용해서는 안된다.
사견으로 위와 같이 기반클래스의 포인터로 메모리 해제만 안하면 별 문제없는것 같다. 즉 처음부터 끝까지 상속받은 클래스의 포인터를 사용하면 된다.
물론 virtual 소멸자가 아니지만 기반클래스로 제작된 클래스도 있다.
ex) 부스트의 noncopyable

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다