6

数時間前、メモリリークの問題をいじっていましたが、仮想デストラクタに関する基本的な情報が間違っていることがわかりました。クラスのデザインについて説明します。

class Base
{
  virtual push_elements()
  {}
};

class Derived:public Base
{
vector<int> x;
public:
   void push_elements(){ 
      for(int i=0;i <5;i++)
         x.push_back(i); 
   }
};

void main()
{
    Base* b = new Derived();
    b->push_elements();
    delete b;
}

境界チェッカーツールは、派生クラスベクトルのメモリリークを報告しました。そして、デストラクタは仮想ではなく、派生クラスのデストラクタは呼び出されないことがわかりました。そして、驚くべきことに、デストラクタを仮想化したときに修正されました。派生クラスのデストラクタが呼び出されなくても、ベクトルは自動的に割り当て解除されませんか?それはBoundsCheckerツールの癖ですか、それとも仮想デストラクタの理解が間違っていますか?

4

8 に答える 8

15

基本クラスに仮想デストラクタがない場合に基本クラスポインタを介して派生クラスオブジェクトを削除すると、未定義の動作が発生します。

観察したこと(オブジェクトの派生クラス部分が破棄されることはなく、したがってそのメンバーの割り当てが解除されることもありません)は、考えられる多くの動作の中でおそらく最も一般的であり、デストラクタがこのようにポリモーフィズムを使用すると、仮想になります。

于 2010-04-27T15:34:02.530 に答える
8

基本クラスに仮想デストラクタがない場合、コードの結果は未定義の動作であり、必ずしも間違ったデストラクタが呼び出されるとは限りません。これはおそらくBoundsCheckerが診断しているものです。

于 2010-04-27T15:34:36.187 に答える
2

これは技術的には定義されていませんが、診断するには、最も一般的な障害の方法を知る必要があります。失敗の一般的な方法は、間違ったデストラクタを呼び出すことです。確かに私は2つの実装しか使用していませんが、他の方法で失敗する実装を知りません。

これが発生する理由は、非仮想メンバー関数をオーバーライドしてベースポインターを介して呼び出そうとしたときに、「間違った」関数が呼び出されるのと同じ理由です。

于 2010-04-27T15:46:09.770 に答える
1

デストラクタが仮想でない場合は、ベースデストラクタが呼び出されます。ベースデストラクタはベースオブジェクトをクリーンアップして終了します。ベースオブジェクトデストラクタが派生オブジェクトについて知る方法はありません。それは呼び出される派生デストラクタである必要があります。これを行う方法は、他の関数と同様に、デストラクタを仮想化することです。

于 2010-04-27T15:32:53.897 に答える
1

C ++ FAQ Liteから:「デストラクタはいつ仮想化する必要がありますか?」ここでそれを読んでください。(ちなみに、C ++ FAQ Liteは、C ++に関連するすべての質問の優れた情報源です)。

于 2010-04-27T16:17:13.150 に答える
1

C ++では、トリビアルデストラクタは再帰的に定義された概念です。これは、クラスのすべてのメンバー(およびすべての基本クラス)にトリビアルデストラクタがある場合にコンパイラが作成したデストラクタです。(トリビアルコンストラクターと呼ばれる同様の概念があります。)

自明でないデストラクタを持つオブジェクトがオブジェクトに含まれている場合(vectorあなたの例のように)、inの外部オブジェクト(あなたのようなDerived)のデストラクタはもはや自明ではありません。デストラクタを作成しなかった場合でも、C ++コンパイラは、デストラクタを持つメンバーのデストラクタを呼び出すデストラクタを自動的に作成しました。

したがって、何も記述していなくても、非仮想デストラクタを記述する際の注意事項が適用されます。

于 2010-04-27T16:17:45.553 に答える
1

あなたがc#から来ているなら、あなたはなぜベクトルが自動的に割り当て解除されないのか疑問に思っていました。ただし、c ++では、Microsoft Manged Extesions to C ++(C ++ / CLI)を使用しない限り、自動メモリ管理は使用できません。

基本クラスには仮想のデストラクタがないため、派生クラスオブジェクトが解放されることはなく、派生クラスのベクターデータメンバーに割り当てられたメモリがリークされます。

于 2010-11-06T00:11:05.650 に答える
0

デストラクタはクラスのメンバー関数であり、その名前はクラス名と同じ名前であり、その前にチルダ記号(〜)が付いています。デストラクタは、オブジェクトがスコープ外になったときにクラスのオブジェクトを破棄するために使用されます。または、クラス破棄のすべてのクリーンアップはデストラクタで実行されると言うことができます。クラス内のオブジェクトの構築中にすべてのメモリが割り当てられると、オブジェクトがスコープ外になると破棄(またはメモリ解放)されます。

BoundsCheckの例で詳細を確認してください

于 2010-06-29T18:32:11.513 に答える