C++でベースクラスの仮想デストラクタを宣言することは良い習慣ですが、インターフェイスとして機能する抽象クラスでもvirtual
デストラクタを宣言することは常に重要ですか?いくつかの理由と理由を提供してください。
インターフェイスにとってはさらに重要です。クラスのユーザーは、おそらく具体的な実装へのポインタではなく、インターフェイスへのポインタを保持します。デストラクタが非仮想である場合、それらが削除されると、派生クラスのデストラクタではなく、インターフェイスのデストラクタ(または、指定しない場合はコンパイラが提供するデフォルト)を呼び出します。インスタントメモリリーク。
例えば
class Interface
{
virtual void doSomething() = 0;
};
class Derived : public Interface
{
Derived();
~Derived()
{
// Do some important cleanup...
}
};
void myFunc(void)
{
Interface* p = new Derived();
// The behaviour of the next line is undefined. It probably
// calls Interface::~Interface, not Derived::~Derived
delete p;
}
あなたの質問に対する答えはしばしばありますが、常にではありません。抽象クラスがクライアントへのポインタでdeleteを呼び出すことを禁止している場合(またはドキュメントでそう言っている場合)、仮想デストラクタを宣言しないことは自由です。
デストラクタを保護することにより、クライアントへのポインタでdeleteを呼び出すことを禁止できます。このように動作するため、仮想デストラクタを省略することは完全に安全で合理的です。
最終的には仮想メソッドテーブルがなくなり、クライアントへのポインタを介してそれを削除不可にすることをクライアントに通知することになります。そのため、これらの場合は仮想と宣言しない理由があります。
[この記事のアイテム4を参照してください: http://www.gotw.ca/publications/mill18.htm ]
私はいくつかの研究を行い、あなたの答えを要約しようとすることにしました。次の質問は、必要なデストラクタの種類を決定するのに役立ちます。
これがお役に立てば幸いです。
* C++にはクラスをfinal(つまり、サブクラス化不可)としてマークする方法がないことに注意することが重要です。したがって、デストラクタを非仮想およびパブリックとして宣言する場合は、派生することを同僚のプログラマに明示的に警告することを忘れないでくださいあなたのクラスから。
参照:
はい、それは常に重要です。派生クラスはメモリを割り当てたり、オブジェクトが破棄されたときにクリーンアップする必要がある他のリソースへの参照を保持したりできます。インターフェイス/抽象クラスに仮想デストラクタを与えない場合、ベースクラスハンドルを介して派生クラスインスタンスを削除するたびに、派生クラスのデストラクタは呼び出されません。
したがって、あなたはメモリリークの可能性を開いています
class IFoo
{
public:
virtual void DoFoo() = 0;
};
class Bar : public IFoo
{
char* dooby = NULL;
public:
virtual void DoFoo() { dooby = new char[10]; }
void ~Bar() { delete [] dooby; }
};
IFoo* baz = new Bar();
baz->DoFoo();
delete baz; // memory leak - dooby isn't deleted
always必須ではありませんが、良い習慣であると思います。それがすることは、派生型のオブジェクトを基本型のポインタを通して安全に削除できるようにすることです。
たとえば、次のとおりです。
Base *p = new Derived;
// use p as you see fit
delete p;
Base
に仮想デストラクタがない場合は、オブジェクトがBase *
であるかのようにオブジェクトを削除しようとするため、不正な形式です。
それは良い習慣だけではありません。すべてのクラス階層のルール#1です。
理由は次のとおりです。典型的な動物の階層を取ります。仮想デストラクターは、他のメソッド呼び出しと同様に仮想ディスパッチを実行します。次の例をご覧ください。
Animal* pAnimal = GetAnimal();
delete pAnimal;
Animalは抽象クラスであると仮定します。 C++が呼び出す適切なデストラクタを認識する唯一の方法は、仮想メソッドのディスパッチです。デストラクタが仮想でない場合は、単にAnimalのデストラクタを呼び出し、派生クラスのオブジェクトを破棄しません。
基本クラスでデストラクタを仮想化する理由は、派生クラスから選択を単純に削除するためです。デストラクタはデフォルトで仮想になります。
答えは簡単です。仮想である必要があります。そうでなければ、基本クラスは完全なポリモーフィッククラスではありません。
Base *ptr = new Derived();
delete ptr; // Here the call order of destructors: first Derived then Base.
上記の削除をお勧めしますが、基本クラスのデストラクタが仮想ではない場合、基本クラスのデストラクタのみが呼び出され、派生クラスのすべてのデータは削除されないままになります。