これは、Effective C++(Meyers)によるものです。
基本クラスとして設計されていないクラス、または多態的に使用されるように設計されていないクラスは、仮想デストラクタを宣言しないでください
ポリモーフィックでないクラスが仮想デストラクタを宣言してはならない理由がわかりません。
仮想関数を持たない親クラスと子クラスがあり、子オブジェクトへの親クラスポインターがあると仮定します。親クラスポインターで削除を呼び出すと、親デストラクタのみが呼び出されます。子デストラクタも呼び出したいのですが。
ここのキーワードは設計されていません
クラス設計されていないが基本クラスになる、または設計されていない多形で使用される
仮想関数/デストラクタは無料ではありません。これらはパフォーマンスのオーバーヘッドを引き起こすため、すべての場合にそれらを使用する必要はありません。ただし、非仮想デストラクタを持つ基本クラスへのポインタを使用して派生クラスオブジェクトを削除すると、未定義の動作が発生します。
http://www.geeksforgeeks.org/g-fact-37/
つまり、パフォーマンスと安全性のトレードオフです。
仮想関数を持たない親クラスと子クラスがあり、子オブジェクトへの親クラスポインターがあるとします
次に、それを多態的に使用しています。
私はそれをすることにはあまり意味がありませんが。仮想メンバーがなければ、基本クラスだけを使用する場合と比較して、サブクラスはプログラムの動作に影響を与える機会があまりありません。
非ポリモーフィッククラスは、派生クラスのオブジェクトを指すために使用されないクラスです。これらは、派生しないクラス(C++では大多数です)か、完全なインターフェイスを提供せずにコードを再利用するためのベースとして使用されるクラスです(例 std::iterator
)。 =;ほとんどのイテレータのパブリックベースクラスですが、std::iterator *
)を作成しても意味がありません。
具体的な例として、次のようなものを想定します。
class Base
{
...
int some_int;
};
class Derived: public Base
{
...
vector<int> some_vec; // the presence of this field makes `Derived`
// no longer trivially destructible.
};
ここでこれを実行すると(図のように直接、またはスマートポインターを介して間接的に)、次のようになります。
Base* base = new Derived;
...
delete base;
...次にsome_vec
は、Base
が仮想デストラクタを定義しない限り破棄されません。結局のところ、ベースポインタしか与えられていないため、システムは、その機能を指す仮想テーブルがない限り、実行時にDerived
に関する情報が不足しているため、動的ディスパッチが発生して情報/機能固有に到達できます。適切に破壊するための特定の要件のようにDerived
に変更します。
ベースポインターisポリモーフィック動作を介してオブジェクトを削除し、未定義の動作を実行せずに安全かつ正しく行うには、基本クラスで仮想を定義する必要がありますデストラクタ。
最終的に問題は、ベースポインタを介してオブジェクトを削除するかどうかということになります。その場合は、パブリック仮想デストラクタを定義します。
class BaseSafeDelete
{
public:
// Provides safe destruction through a base pointer.
virtual ~BaseSafeDelete() {}
};
そうでない場合は、保護された非仮想デストラクタを定義します。
class BaseNoDelete
{
protected:
// Prevents deleting outright through a base pointer.
/*nonvirtual*/ ~BaseNoDelete() {}
};
そして最も重要なこととして、継承は、基本クラスを設計するときに事前に設計および決定されるものです。継承のために設計されていないクラス(最初の記号として保護された非仮想dtorまたはパブリック仮想dtorのいずれかを欠く)を取り、それを拡張しようとする後付けとしては適切ではありません。そのためには、 複合再利用の原則 を参照してください。