Apakah menghapus pada pointer ke subkelas memanggil destruktor kelas dasar?

165

Saya memiliki class Ayang menggunakan alokasi memori tumpukan untuk salah satu bidangnya. Kelas A dipakai dan disimpan sebagai bidang pointer di kelas lain ( class B.

Ketika saya selesai dengan objek kelas B, saya sebut delete, yang saya anggap memanggil destruktor ... Tapi apakah ini memanggil destruktor kelas A juga?

Edit:

Dari jawaban, saya ambil itu (harap edit jika salah):

  1. delete dari contoh panggilan B B :: ~ B ();
  2. yang memanggil A::~A();
  3. A::~A harus secara eksplisit deletesemua variabel anggota yang dialokasikan tumpukan dari objek A;
  4. Akhirnya blok memori yang menyimpan instance kelas B dikembalikan ke heap - ketika baru digunakan, pertama dialokasikan blok memori pada heap, kemudian memanggil konstruktor untuk menginisialisasi, sekarang setelah semua destructor telah dipanggil untuk menyelesaikan objek yang blokir tempat objek berada dikembalikan ke heap.
Nick Bolton
sumber

Jawaban:

183

Destruktor A akan berjalan ketika masa hidupnya berakhir. Jika Anda ingin memorinya dibebaskan dan menjalankan destruktor, Anda harus menghapusnya jika dialokasikan pada heap. Jika dialokasikan pada tumpukan ini terjadi secara otomatis (yaitu ketika keluar dari ruang lingkup; lihat RAII). Jika itu adalah anggota kelas (bukan pointer, tetapi anggota penuh), maka ini akan terjadi ketika objek yang berisi dihancurkan.

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

Dalam contoh di atas, setiap hapus dan hapus [] diperlukan. Dan tidak perlu menghapus (atau memang bisa digunakan) di mana saya tidak menggunakannya.

auto_ptr, unique_ptrdan shared_ptrlain - lain ... sangat bagus untuk membuat manajemen seumur hidup ini lebih mudah:

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
Gerhana
sumber
Saya ingin tahu apakah destruktor dipanggil ketika Anda membebaskan memori hanya sebagian (mis. Menggunakan pointer yang salah)
Tomáš Zato - Reinstate Monica
Pointer hanyalah angka. Anda bahkan dapat secara tidak sengaja menggunakan ++operator di dalamnya. Jadi saya bertanya-tanya apakah pointer yang menunjuk di tengah data kelas masih memiliki efek.
Tomáš Zato - Reinstate Monica
2
@ TomášZato: Jika Anda memanggil delete pada pointer acak, maka Anda sedang kacau. Tidak pernah ada alasan bagus untuk melakukan itu. Bahkan, jika Anda secara manual memanggil delete di mana saja selain dari smart-pointer destructor, maka Anda mungkin ingin melihat mengapa Anda tidak menggunakan smart pointer, atau manajer objek lainnya.
Eclipse
shared_array hanya dari boost, ya?
Dronz
30

Ketika Anda memanggil delete pada sebuah pointer yang dialokasikan oleh yang baru, destructor dari objek yang ditunjuk akan dipanggil.

A * p = new A;

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

sumber
22

Ini dinamai "destructor", bukan "deconstructor".

Di dalam destruktor dari setiap kelas, Anda harus menghapus semua variabel anggota lainnya yang telah dialokasikan dengan yang baru.

sunting: Untuk memperjelas:

Katakan sudah

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;
}

Mengalokasikan instance B dan kemudian menghapus itu bersih, karena apa yang dialokasikan B secara internal juga akan dihapus dalam destruktor.

Tetapi instance dari kelas C akan membocorkan memori, karena mengalokasikan instance dari A yang tidak dilepaskan (dalam hal ini C bahkan tidak memiliki destruktor).

Sebastian Mach
sumber
5

Jika Anda memiliki pointer biasa ( A*) maka destructor tidak akan dipanggil (dan memori Amisalnya tidak akan dibebaskan juga) kecuali Anda melakukannya deletesecara eksplisit di Bdestructor. Jika Anda ingin penghancuran otomatis lihat di pointer pintar suka auto_ptr.

sharptooth
sumber
4

Anda harus menghapus A sendiri di destructor B.

corné
sumber
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;
    }
};

Saat kamu melakukan:

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

Destruktor akan dipanggil hanya jika kelas dasar Anda memiliki kata kunci virtual.

Maka jika Anda tidak memiliki destruktor virtual saja ~ B () akan dipanggil. Tetapi karena Anda memiliki destruktor virtual, pertama ~ D () akan dipanggil, kemudian ~ B ().

Tidak ada anggota B atau D yang dialokasikan pada heap yang akan dibatalkan alokasi kecuali Anda secara eksplisit menghapusnya. Dan menghapus mereka akan memanggil destructor mereka juga.

Brian R. Bondy
sumber
1

Anda punya sesuatu seperti

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

Jika Anda menelepon delete b;, tidak ada yang terjadi pada, dan Anda memiliki kebocoran memori. Mencoba untuk mengingat delete b->a;bukanlah solusi yang baik, tetapi ada beberapa yang lain.

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

Ini adalah destruktor untuk B yang akan menghapus a. (Jika a adalah 0, penghapusan itu tidak menghasilkan apa-apa. Jika a bukan 0 tetapi tidak menunjuk ke memori dari yang baru, Anda mendapatkan banyak tumpukan.)

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

Dengan cara ini Anda tidak memiliki sebagai penunjuk, melainkan auto_ptr <> (shared_ptr <> akan melakukannya juga, atau pointer pintar lainnya), dan itu secara otomatis dihapus ketika b.

Salah satu dari cara ini bekerja dengan baik, dan saya telah menggunakan keduanya.

David Thornley
sumber
1

Saya bertanya-tanya mengapa destruktor kelas saya tidak dipanggil. Alasannya adalah saya lupa memasukkan definisi kelas itu (#include "class.h"). Saya hanya punya deklarasi seperti "kelas A;" dan kompiler senang dengan itu dan izinkan saya memanggil "hapus".

Harri Luoma
sumber
Tingkatkan tingkat peringatan kompiler
Phil1970
0

Tidak. Pointer akan dihapus. Anda harus memanggil delete pada A secara eksplisit di destructor B.

Rvdk
sumber
Saya melakukan ini, pertanyaan saya adalah apakah destructor dipanggil?
Nick Bolton
0

Destructor untuk objek kelas A hanya akan dipanggil jika delete dipanggil untuk objek itu. Pastikan untuk menghapus pointer itu di destructor kelas B.

Untuk sedikit informasi lebih lanjut tentang apa yang terjadi ketika delete dipanggil pada sebuah objek, lihat: http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.9

Kris Kumler
sumber
0

tidak itu tidak akan memanggil destruktor untuk kelas A, Anda harus menyebutnya secara eksplisit (seperti yang dikatakan PoweRoy), hapus baris 'delete ptr;' misalnya untuk membandingkan ...

  #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;
  }
Darius Kucinskas
sumber