C++ 상속시 소멸자에 virtual?

시작

C++ 상속시 소멸자에 virtual 을 주의깊게 써야한다.
분명히 어디선가 본 글과 코드가 기억납니다. 소멸자에 virtual 을 반드시 써야하는 경우가 있다는 점이 매우 인상적이라 뇌리에 남아있었고, 이 기억때문에 큰 사고(?)를 막은 적도 있어서 정리해두기 위해 글을 남깁니다.

문제

문제를 만들어내는 상황을 설계해보도록 하겠습니다.

  1. Class A
  2. Class B : A 상속
  3. B 객체 생성
  4. A로 캐스팅
  5. a 소멸
    1. B로 만들었으니 A로 캐스팅해도 B로 소멸?
    2. A로 캐스팅하였으니 A로 소멸? 그럼 B가 가진 것들은 어떻게?
물론 5.1 상황이라면 얼마나 편리하겠습니까만 그럴리가 없죠. 그러니 A로 소멸하고 B일때 가진 것들은 반납되지 않는 문제가 발생합니다. 예를 들어 메모리누수라던가 리소스를 반납 안하던가..생각만해도 끔찍하군요. 코드는 다음과 같습니다.
class A
{
public:
    A() { cout << "Constructor A" << endl; }
    ~A() { cout << "Destructor A" << endl; }
};
class B : A
{
public:
    B() { cout << "Constructor B" << endl; }
    ~B() { cout << "Destructor B" << endl; }
};
int main(int argc, char* argv[])
{
    B* b = new B;
    A* a = (A*)b;
    delete a;
    return 0;
}
결과는 다음과 같습니다. 
$ ./a.out
Constructor A
Constructor B
Destructor A
Destructor B가 호출되지 않는 중요한 문제를 발생시킵니다.

올바른 방법

제목과 같이 소멸자를 virtual로 생성하면 됩니다.
...
virtual ~A() { cout << "Destructor A" << endl; }
...
virtual ~B() { cout << "Destructor B" << endl; }
...

결과는 다음과 같이 B소멸자 호출 후 자동으로 A소멸자까지 호출됩니다.
$ ./a.out
Constructor A
Constructor B
Destructor B
Destructor A

결론

몰라도 문제없는 경우가 많지만, 몰랐을때 큰 문제를 만들어낼 수 있습니다. 이런 부분들을 잘 파악하고 있는 것이 숙련자와 비숙련자를 가리는 큰 차이라고 봅니다. 한동안 C/C++ 을 내팽개치고 딴짓만 하다가 결국 다 포기하고 C/C++로 돌아온 이유입니다.
하던거나 제대로 잘하자.

댓글

이 블로그의 인기 게시물

WSL2 Ubuntu 20.04 및 네트워크 설정

리눅스 멀티코어를 사용하는 tar 압축/해제

git pull 을 했더니 branch가 갈라지는 경우