20

クラスがメモリを動的に割り当てない場合、仮想デストラクタが必要ですか?

例えば

class A
{
      private: 
      int a;
      int b;

      public:
      A();
      ~A();
};

class B: public A
{     
      private:
      int c;
      int d;

      public:
      B();
      ~B();
};

この場合、Aのデストラクタを仮想としてマークする必要がありますか?

4

6 に答える 6

31

問題は、クラスがメモリを動的に割り当てるかどうかではありません。これは、クラスのユーザーがAポインターを介してBオブジェクトを割り当て、それを削除した場合です。

A * a = new B;
delete a;

この場合、Aの仮想デストラクタがない場合、C ++標準は、プログラムが未定義の動作を示すと述べています。これは良いことではありません。

この動作は、標準のセクション5.3.5 / 3(ここでは参照delete)で指定されています。

オペランドの静的型が動的型と異なる場合、静的型はオペランドの動的型の基本クラスであり、静的型は仮想デストラクタを持っているか、動作が定義されていません。

于 2010-01-14T17:06:12.650 に答える
20

仮想デストラクタの目的(つまり、デストラクタを仮想にする目的)は、 delete-expressionを介してオブジェクトのポリモーフィックな削除を容易にすることです。デザインでオブジェクトのポリモーフィックな削除が必要ない場合は、仮想デストラクタは必要ありません。あなたの例を参照すると、型Bのポインタを介して型のオブジェクトを削除するA *必要がある場合(ポリモーフィック削除)、階層の上位にある仮想デストラクタが必要になりますA。正式な観点からは、このように見えます。

(ところで、ニールが言ったように、重要なのはクラスオブジェクトの作成/削除方法であり、クラスが内部メモリを管理する方法ではないことに注意してください。)

優れたプログラミング手法については...それは最終的にはあなたの意図とデザインに依存します。クラスがポリモーフィックになるように設計されていない場合(仮想メソッドがまったくない場合)、仮想デストラクタは必要ありません。クラスがポリモーフィック(少なくとも1つの仮想メソッドを持つ)の場合、「万が一に備えて」デストラクタを仮想化することは非常に良い考えであり、この場合、パフォーマンス/メモリのペナルティは実質的にゼロになります。

後者は通常、かなりよく知られているグッドプラクティスガイドラインとして表されます。クラスに少なくとも1つの仮想メソッドがある場合は、デストラクタも仮想にします。正式な観点からは、仮想デストラクタは実際には必要ないかもしれませんが、それでも従うべきかなり良いガイドラインです。

リソースがないが多態的な階層を形成できるクラスは、階層の最下部に明示的な空の(さらには純粋な)仮想デストラクタを定義するだけで十分であることを除いて、常に空の仮想デストラクタを定義する必要があります。他のすべてのデストラクタは、コンパイラによって暗黙的に定義されている場合でも、自動的に仮想化されます。つまり、すべてのクラスで空のデストラクタを明示的に定義する必要はありません。ベースだけで十分です。

于 2010-01-14T17:10:40.690 に答える
4

メモリの解放は、デストラクタが実行できる唯一の重要な機能ではありません。たとえば、グローバル状態をリセットするためにも使用できます。これを行わなくてもメモリがリークすることはありませんが、プログラムで他の問題が発生する可能性があります。

さらに、デストラクタが現在有用なことを何もしていなくても、将来のある時点でそれが行われる可能性があります。継承がある場合、仮想デストラクタを回避する本当の理由はありません。それを追加して、夜はぐっすり眠ってみませんか?

于 2010-01-14T17:05:39.607 に答える
0

デストラクタを仮想として宣言する目的は、Derived型のオブジェクトを指しているBase型のポインタでdeleteを呼び出すたびに、派生クラスのデストラクタを呼び出せるようにすることです。そうしないと、未定義の動作が発生します。

メモリを動的に割り当てていない場合にデストラクタを仮想としてマークする必要がないという仮定は、メモリを動的に割り当てていない場合に派生クラスのデストラクタを呼び出す必要がないことを意味します。これは誤りです。動的に割り当てられたメモリの割り当てを解除する以外に、派生クラスのデストラクタで他のいくつかの操作を実行できる場合があります。例としては、開いているファイルを閉じる、いくつかの情報をログに記録するなどがあります。

于 2010-01-14T17:43:05.227 に答える
0

親クラスのデストラクタは常に自動的に呼び出され、明示的なdtorが宣言されていない場合は、デフォルトのdtorが常に生成されます。あなたの例では、AもBも自明でないdtorを持つ必要はありません。

クラスに仮想関数がある場合、追加の仮想dtorは問題にならず、良い習慣です。クラスがメモリまたはその他のリソース(ファイルを開くなど)を割り当てる場合、破棄時にそのリソースを再度解放するためにdtorが必要です。

于 2010-01-14T17:07:42.943 に答える
-1

唯一の懸念事項がメモリである場合は、基本クラスのデストラクタ(および/または他のクラス)を保護することから始める必要があります。次に、何かがコンパイルされない場合は、その理由がわかります。参照:ブースト::任意の方法。

于 2012-09-06T04:23:01.017 に答える