サブクラスへのポインターでの削除は、基本クラスのデストラクターを呼び出しますか?


165

私は、class Aそのフィールドの1つにヒープメモリ割り当てを使用しています。クラスAはインスタンス化され、別のクラス(class B

クラスBのオブジェクトが完了したら、を呼び出しますdelete。これは、デストラクターを呼び出すと想定しています...しかし、これはクラスAのデストラクターも呼び出しますか?

編集:

答えから、私はそれを採用します(間違っている場合は編集してください):

  1. delete BのインスタンスがB ::〜B();を呼び出す
  2. 呼び出す A::~A();
  3. A::~A deleteAオブジェクトのヒープに割り当てられたすべてのメンバー変数を明示的に指定する必要があります。
  4. 場合-最後のメモリブロックの記憶は、クラスBのインスタンスがヒープに返される前記新しいは、それが最初にすべてのデストラクタがオブジェクトを完成させるために呼び出された今後、それを初期化するコンストラクタを呼び出し、次に、ヒープ上のメモリのブロックを割り当てられ、使用されましたオブジェクトが常駐していたブロックがヒープに返されます。

回答:


183

Aのデストラクタは、その寿命が終了すると実行されます。そのメモリを解放してデストラクタを実行したい場合は、ヒープに割り当てられていた場合は削除する必要があります。スタックに割り当てられた場合、これは自動的に行われます(つまり、スコープから外れたとき。RAIIを参照)。それがクラスのメンバー(ポインターではなく完全なメンバー)である場合、これは包含オブジェクトが破棄されるときに発生します。

class A
{
    char *someHeapMemory;
public:
    A() : someHeapMemory(new char[1000]) {}
    ~A() { delete[] someHeapMemory; }
};

class B
{
    A* APtr;
public:
    B() : APtr(new A()) {}
    ~B() { delete APtr; }
};

class C
{
    A Amember;
public:
    C() : Amember() {}
    ~C() {} // A is freed / destructed automatically.
};

int main()
{
    B* BPtr = new B();
    delete BPtr; // Calls ~B() which calls ~A() 
    C *CPtr = new C();
    delete CPtr;
    B b;
    C c;
} // b and c are freed/destructed automatically

上記の例では、すべてのdeleteおよびdelete []が必要です。そして、私がそれを使用しなかった場合、削除は必要ありません(または実際に使用できます)。

auto_ptrunique_ptrおよびshared_ptr等...はるかに簡単に、この寿命管理を行うための素晴らしいです。

class A
{
    shared_array<char> someHeapMemory;
public:
    A() : someHeapMemory(new char[1000]) {}
    ~A() { } // someHeapMemory is delete[]d automatically
};

class B
{
    shared_ptr<A> APtr;
public:
    B() : APtr(new A()) {}
    ~B() {  } // APtr is deleted automatically
};

int main()
{
    shared_ptr<B> BPtr = new B();
} // BPtr is deleted automatically

私はあなたが部分的にしかメモリを解放するときデストラクタが呼び出された場合だろう(例えば、間違ったポインタを使用して。)
トマーシュZato -復活モニカ

ポインタは単なる数値です。誤って++演算子を使用することもできます。クラスデータの真ん中を指すポインタがまだ効果があるのか​​しら。
トマーシュZato -復活モニカ

2
@TomášZato:ランダムポインターでdeleteを呼び出すと、失敗します。それをする正当な理由は決してありません。実際、スマートポインターデストラクタ以外の場所で手動でdeleteを呼び出している場合は、スマートポインターやその他のオブジェクトマネージャーを使用していない理由をもう一度確認する必要があります。
Eclipse、

shared_arrayはブーストのみからのものです、そうですか?
Dronz、

30

newによって割り当てられたポインタに対してdeleteを呼び出すと、ポイントされたオブジェクトのデストラクタが呼び出されます。

A * p = new A;

delete p;    // A:~A() called for you on obkect pointed to by p

22

「デコンストラクタ」ではなく「デストラクタ」と名付けられています。

各クラスのデストラクタ内では、newで割り当てられた他のすべてのメンバー変数を削除する必要があります。

編集:明確にするために:

あなたが持っていると言う

struct A {}

class B {
    A *a;
public:
    B () : a (new A) {}
    ~B() { delete a; }
};

class C {
    A *a;
public:
    C () : a (new A) {}        
};

int main () {
    delete new B;
    delete new C;
}

Bのインスタンスを割り当ててから削除することは、Bが内部で割り当てるものもデストラクタで削除されるため、クリーンです。

ただし、クラスCのインスタンスは、解放しないAのインスタンスを割り当てるため、メモリをリークします(この場合、Cにはデストラクタさえありません)。


5

通常のポインタ(A*)がある場合、デストラクタで明示的に指定しAない限り、デストラクタは呼び出されません(たとえば、メモリも解放されません)。自動破棄が必要な場合は、などのスマートポインタを確認してください。deleteBauto_ptr


4

BのデストラクタでAを自分で削除する必要があります。


4
class B
{
public:
    B()
    {
       p = new int[1024];  
    }
    virtual ~B()
    {
        cout<<"B destructor"<<endl;
        //p will not be deleted EVER unless you do it manually.
    }
    int *p;
};


class D : public B
{
public:
    virtual ~D()
    {
        cout<<"D destructor"<<endl;
    }
};

あなたがするとき:

B *pD = new D();
delete pD;

デストラクタは、基本クラスにvirtualキーワードがある場合にのみ呼び出されます。

次に、仮想デストラクタがない場合は、〜B()のみが呼び出されます。ただし、仮想デストラクタがあるため、最初に〜D()が呼び出され、次に〜B()が呼び出されます。

ヒープに割り当てられたBまたはDのメンバーは、明示的に削除しない限り割り当て解除されません。また、それらを削除すると、デストラクタも呼び出されます。


1

あなたのようなものがあります

class B
{
   A * a;
}
B * b = new B;
b->a = new A;

次にを呼び出すとdelete b;、aには何も起こらず、メモリリークが発生します。覚えておくのdelete b->a;は良い解決策ではありませんが、他にもいくつかあります。

B::~B() {delete a;}

これはaを削除するBのデストラクタです。(aが0の場合、その削除は何もしません。aが0ではないが、新しいメモリを指していない場合、ヒープが破損します。)

auto_ptr<A> a;
...
b->a.reset(new A);

このように、ポインタとしてaはありませんが、auto_ptr <>(shared_ptr <>や他のスマートポインタも同様に機能します)があり、bがあると自動的に削除されます。

これらの方法のいずれかがうまく機能し、私は両方を使用しました。


1

なぜクラスのデストラクタが呼び出されなかったのかと思っていました。その理由は、そのクラスの定義(#include "class.h")を含めるのを忘れていたからです。「クラスA」のような宣言しかありませんでした。コンパイラーはそれに満足し、「削除」と呼んでもらいました。


コンパイラの警告レベルを上げる
Phil1970、2018

0

いいえ、ポインタは削除されます。Bのデストラクタで明示的にAの削除を呼び出す必要があります。


私はこれをやっています、私の質問はデストラクタが呼び出されたのですか?
ニックボルトン


0

クラスAのデストラクタを呼び出さないので、(PoweRoyが言ったように)明示的に呼び出す必要があります。行 'delete ptr;'を削除してください。比較する例では...

  #include <iostream>

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

  A::~A()
  {
     std::cout << "Destructor of A" << std::endl;
  }

  class B
  {
     public:
        B(){ptr = new A();};
        ~B();
     private:
        A* ptr;
  };

  B::~B()
  {
     delete ptr;
     std::cout << "Destructor of B" << std::endl;
  }

  int main()
  {
     B* b = new B();
     delete b;
     return 0;
  }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.