回答:
それはインターフェースにとってさらに重要です。クラスのユーザーはおそらく、具体的な実装へのポインターではなく、インターフェイスへのポインターを保持します。デストラクタが非仮想である場合、それらが削除されると、派生クラスのデストラクタではなく、インターフェイスのデストラクタ(または、コンパイラが提供するデフォルトを指定しなかった場合)を呼び出します。インスタントメモリリーク。
例えば
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;
}
[expr.delete]/
:... if the static type of the object to be deleted is different from its dynamic type, ... the static type shall have a virtual destructor or the behavior is undefined. ...
。Derivedが暗黙的に生成されたデストラクタを使用した場合でも、未定義になります。
あなたの質問への答えはしばしばですが、いつもではありません。抽象クラスがクライアントへのポインタでdeleteを呼び出すことを禁止している場合(またはドキュメントにそのように記載されている場合)、仮想デストラクタを宣言しないことは自由です。
デストラクタを保護することで、クライアントがポインタを削除するのを禁止できます。このように機能するため、仮想デストラクタを省略することは完全に安全で合理的です。
最終的には仮想メソッドテーブルがなくなり、ポインタを介してそれを削除不可にする意図をクライアントに通知することになるので、これらのケースでは実際に仮想メソッドを宣言しない理由があります。
[この記事の項目4を参照してください:http : //www.gotw.ca/publications/mill18.htm ]
私はいくつかの調査を行い、あなたの答えを要約しようとすることにしました。次の質問は、必要なデストラクタの種類を決定するのに役立ちます。
これがお役に立てば幸いです。
* C ++ではクラスを最終(つまりサブクラス化不可)としてマークする方法がないことに注意することが重要です。そのため、デストラクタを非仮想かつパブリックとして宣言する場合は、他のプログラマに対して明示的に警告することを忘れないでください。クラスから派生します。
参照:
はい、それは常に重要です。派生クラスは、メモリを割り当てたり、オブジェクトが破棄されたときにクリーンアップする必要がある他のリソースへの参照を保持したりできます。インターフェース/抽象クラスに仮想デストラクタを指定しない場合、基本クラスハンドルを介して派生クラスインスタンスを削除するたびに、派生クラスのデストラクタは呼び出されません。
したがって、メモリリークの可能性を切り開いています
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
常に必要なわけではありませんが、良い方法だと思います。それが何をするか、それは派生型が基本型のポインタを通して安全に削除されることを可能にすることです
だから例えば:
Base *p = new Derived;
// use p as you see fit
delete p;
がBase
仮想デストラクタを持たない場合は、形式が正しくありませんBase *
。オブジェクトがであるかのようにオブジェクトを削除しようとするためです。
shared_ptr
ように、オブジェクトを削除しようBase *
とします。作成したもののタイプを記憶しています。参照されているリンク、特に「デストラクタは、Tに仮想デストラクタがないか、またはvoidである場合でも、元の型で完全な同じポインタで削除を呼び出す」と書かれているビットを参照してください。
それは良い習慣だけではありません。これは、クラス階層のルール#1です。
なぜか。典型的な動物の階層を取り上げます。仮想デストラクタは、他のメソッド呼び出しと同じように仮想ディスパッチを実行します。次の例を見てください。
Animal* pAnimal = GetAnimal();
delete pAnimal;
Animalは抽象クラスであると仮定します。C ++が呼び出す適切なデストラクタを認識する唯一の方法は、仮想メソッドディスパッチを介することです。デストラクタが仮想でない場合は、単に動物のデストラクタを呼び出し、派生クラスのオブジェクトは破棄しません。
基本クラスでデストラクタを仮想化する理由は、派生クラスから選択肢を単に削除するためです。デストラクタはデフォルトで仮想になります。
答えは単純です。仮想である必要があります。そうでなければ、基本クラスは完全な多態性クラスではありません。
Base *ptr = new Derived();
delete ptr; // Here the call order of destructors: first Derived then Base.
上記の削除をお勧めしますが、基本クラスのデストラクタが仮想でない場合は、基本クラスのデストラクタのみが呼び出され、派生クラスのすべてのデータは削除されずに残ります。
delete p
未定義の動作を呼び出します。の呼び出しは保証されていませんInterface::~Interface
。