基底クラスが仮想デストラクタの持たないクラス2011年11月30日 14時17分06秒

継承しているからと言って、全ての基底クラスのデストラクタを仮想化しなければいけないわけではない。

C 言語との互換性や、IPC 用に基底クラスを POD 化しておいて、バイナリのまま通信したい場合もある。時には後方互換として仮想化すると問題のあるコードがあるため、不可能な時もある。

そこで、C++ のデストラクタの実験。A、B、C と三つクラスが継承関係にある。A のデストラクタは仮想化されていない。


% cat destructor.cpp
#include <iostream>

struct A
{
   A(){ std::cout << "A" << std::endl; }
   ~A(){ std::cout << "~A" << std::endl; }
};

struct B: public A
{
   B(){ std::cout << "B" << std::endl; }
   virtual ~B(){ std::cout << "~B" << std::endl; }
};

struct C: public B
{
   C(){ std::cout << "C" << std::endl; }
   virtual ~C(){ std::cout << "~C" << std::endl; }
};

int main()
{
   std::cout << "==> B* b" << std::endl;
   B* b = new C();
   delete b;
   std::cout << "==> A* a" << std::endl;
   A* a = new C();
   delete a;
}

結果。

% g++ destructor.cpp
% ./a.out 
==> B* b
A
B
C
~C
~B
~A
==> A* a
A
B
C
~A

B へのポインタに C を割り当てた場合では、C を破棄する時にしっかりと C から A までの全てのデストラクタが呼ばれる。B が起点になり、C のデストラクタから実行する。それに引き替え、A へのポインタを通して破棄したときは、当然、B と C のデストラクタは呼ばれない。

要は使い方次第なのだが、コードの量が多くなると把握しづらい事。多相性と継承を多用しつつ、この様なコードを入れると、勘違いして正しい動作を把握できない、または理解できない者達が少なくないので、止めておいた方が無難な様だ。