2011-07-08 17 views
23

C++では、少なくとも1つの他の仮想関数を持つクラスに対してデストラクタをデフォルトで仮想化するのはなぜですか?この場合、仮想デストラクタを追加すると何も費用がかかりません。 C++ 0xはこれに対処しますか?デストラクタはデフォルトでは仮想ではありません[C++]

+2

キーワードは 'ほとんど'です。あなたのベースに仮想関数があり、仮想デストラクタに支払うことを望まない場合、この新しい世界では仮想ではないことをどのように指定しますか?また、古いコードはどうなりますか?下位互換性の問題に対処する計画が必要です。 –

+4

仮想デストラクタは、すべての派生クラスに対して、デストラクタコードの別のコピーが必要であるという点でコストがあります。 [この質問](http://stackoverflow.com/questions/6613870/gnu-gcc-g-why-does-it-generate-multiple-dtors/6614903)を参照してください。 –

+0

可能な複製[なぜcppで仮想としてすべての機能を持たないのですか](http://stackoverflow.com/questions/6606657/why-not-have-all-the-functions-as-virtual-in-cpp) – iammilind

答えて

19

あなたはあなたが必要でないものを支払うことはありません。ベースポインタを使用して決して削除しない場合、間接的なデストラクタ呼び出しのオーバーヘッドを望んでいない可能性があります。

おそらく、vtableの存在が唯一のオーバーヘッドであると考えていた可能性があります。しかし、個々の関数のディスパッチも考慮する必要があります。デストラクタコールのディスパッチを直接行いたい場合は、そのようにする必要があります。

ベースポインタを削除してそのクラスに仮想メソッドがあると警告するのはあなたのコンパイラにとってはいいと思います。

編集:ここでSimonの優れたコメントを引き出せます:デストラクタ用に生成されたコードでthis SO questionをチェックしてください。ご覧のように、コードブールオーバーヘッドも考慮されます。

2

標準の手紙では、非仮想的なデストラクタを持つ多相クラスはバグではありません。そのようなオブジェクトに対して実行される特定のアクションの1つは、定義されていない動作になりますが、他のすべては完全に正直なものです。だから、プログラマが間違いを犯すという点で、標準の他の寛大な動作を考えれば、なぜデストラクタに特別な扱いを与えなければならないのでしょうか?

このような変更にはコストがかかりますが、仮想テーブルは1つの要素が大きくなり、仮想ディスパッチはデストラクタ呼び出しに関連付けられます。

私の知る限りでは、いいえ、この点についてはC++ 11ではデストラクタの動作に変更はありません。私はそれが特別なメンバー機能のセクションで何かを言うだろうと思っていますが、そうではありませんし、同様に一般的な仮想関数のセクションには何もありません。ここで

+0

仮想デストラクタをC++ 0xのデフォルトにする予定はありましたか?私はこのペーパーに出会ったので尋ねます:http://www2.research.att.com/~bs/C++0x_panel.pdfそこには底があったと言われていますが、私はこれ以上の情報を見つけることができませんでしたそれについて。 – jeffythedragonslayer

+1

@dacode:私は実際の会議などに従わなかったので、私は知らない。 4つの "恥ずかしさ"のうち、最後のものだけが実際に変更されました。誰かがそのような変更のためのプロポーザルを提出した場合、私は驚かないだろうが、なぜそれが投票できなかったかは私が言うことはできない。 –

2

は(私はそのようなコードを書くことをお勧めしますしないように)例を示します

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がタイプ消去を使用するため可能です。最後のstd::shared_ptrが破壊を引き起こす場合でも、deleteへの最後の呼び出しはderived*を削除します。std::shared_ptr<void>です。

std::shared_ptrのこの動作は、ではありません。仮想破壊に合わせてあります。それは様々な他の用途(例えば、std::shared_ptr<FILE> { std::fopen(...), std::fclose })を有する。しかし、このテクニックはすでにが働くために間接費を支払うので、基本クラスの仮想デストラクタを持つことに興味がないユーザもいます。それは「あなたが必要なものだけを支払う」という意味です。を意味します。

関連する問題