C++が、少なくとも 1 つの他の仮想関数を持つクラスのデストラクタをデフォルトで仮想にしないのはなぜですか? この場合、仮想デストラクタを追加してもコストはかからず、仮想デストラクタがないことは (ほとんど?) 常にバグです。C++0x はこれに対処しますか?
3 に答える
必要のないものにはお金を払いません。ベース ポインターを介して削除しない場合は、間接デストラクタ呼び出しのオーバーヘッドが必要ない場合があります。
おそらく、vtable の存在だけが唯一のオーバーヘッドだと考えていたのでしょう。しかし、個々の関数のディスパッチも考慮する必要があり、デストラクタでディスパッチを直接呼び出したい場合は、そうすることが許可されている必要があります。
基本ポインタを削除し、そのクラスに仮想メソッドがある場合は、コンパイラが警告するのはいいことだと思います。
編集: Simon の優れたコメントをここに引用させてください:デストラクタ用に生成されたコードに関するこの SO の質問を確認してください。ご覧のとおり、考慮すべきコードの肥大化のオーバーヘッドもあります。
以下に例を示します (そのようなコードを書くことはお勧めしません)。
struct base {
virtual void foo() const = 0;
virtual void bar () const = 0;
};
struct derived: base {
void foo() const {}
void bar() const {}
};
std::shared_ptr<base>
make_base()
{
return std::make_shared<derived>();
}
これは、UB を示さない完全に優れたコードです。これは、std::shared_ptr
型消去を使用するために可能です。最後に を呼び出すと、最後に破棄をトリガーするのが のタイプであっても、delete
a が削除されます。derived*
std::shared_ptr
std::shared_ptr<void>
のこの動作は仮想破壊に合わせたものでstd::shared_ptr
はないことに注意してください。他にもさまざまな用途があります (例: std::shared_ptr<FILE> { std::fopen( ... ), std::fclose }
)。ただし、この手法はすでに間接的な動作の代償を払っているため、一部のユーザーは、基本クラスに仮想デストラクタを使用することに関心がない場合があります。それが「必要な分だけ支払う」ということです。
標準の文字により、非仮想デストラクタを持つポリモーフィック クラスはバグではありません。このようなオブジェクトに対して特定のアクションを実行すると、未定義の動作が発生しますが、それ以外は完全にコーシャです。では、プログラマーが犯す可能性のある間違いという点で標準の振る舞いが寛容であるとすれば、なぜデストラクタに特別な扱いをする必要があるのでしょうか?
そして、そのような変更にはコストがかかりますが、ほとんどは些細なものですが、仮想テーブルは 1 要素大きくなり、仮想ディスパッチはデストラクタ呼び出しに関連付けられます。
私の知る限りでは、C++11 ではこの点に関してデストラクタの動作に変更はありません。特別なメンバー関数のセクションで何かを言うと思いますが、そうではなく、一般的な仮想関数のセクションにも同様に何もありません。