Bir alt sınıfa işaretçi silmek temel sınıf yıkıcısını çağırır mı?


165

Ben bir class Aalanlarından biri için yığın bellek ayırma kullanır. Sınıf A başlatılır ve başka bir sınıfta işaretçi alanı olarak depolanır ( class B.

B sınıfı bir nesneyle işimi bitirdiğimde delete, yıkıcı olarak adlandırdığımı farz ediyorum ... Ama bu A sınıfının yıkıcısı mı diyor?

Düzenle:

Cevaplardan, bunu alıyorum (yanlışsa lütfen düzenleyin):

  1. delete B çağrılarının bir örneğinin B :: ~ B ();
  2. hangi çağrılar A::~A();
  3. A::~A gerektiğini açıkça deletebir nesnenin tüm yığın ayrılmış üye değişkenleri;
  4. Son olarak, söz konusu B sınıfı örneğini saklayan bellek bloğu yığına geri döndürülür - yeni kullanıldığında, ilk olarak öbek üzerinde bir bellek bloğu tahsis eder, sonra tüm yıkıcılar nesneyi sonlandırmak için çağrıldıktan sonra inşaatçıları çağırmaya çağırır. saklanan nesnenin öbeğe döndürüldüğü blok.

Yanıtlar:


183

A'nın yıkıcısı ömrü bittiğinde koşacaktır. Belleğinin boşaltılmasını ve yıkıcının çalışmasını istiyorsanız, yığınta tahsis edilmişse silmeniz gerekir. Yığına tahsis edilmişse, bu otomatik olarak gerçekleşir (yani kapsam dışına çıktığında; bkz. RAII). Bir sınıfın üyesiyse (işaretçi değil, tam üye), bu, içerilen nesne yok edildiğinde gerçekleşir.

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

Yukarıdaki örnekte, her silme ve silme [] gereklidir. Ve kullanmadığım yerde herhangi bir silme işlemine (veya gerçekten de kullanılabilir) gerek yoktur.

auto_ptr, unique_ptrVe shared_ptrvb ... çok daha kolay bu ömür yönetimini yapmak için vardır:

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

Hafızayı sadece kısmen
boşaltıldığında

İşaretçi sadece bir sayıdır. Kazara ++operatörü bile kullanabilirsiniz . Bu yüzden merak ediyorum sınıf verilerinin ortasında işaretçi hala etkisi var.
Tomáš Zato - Monica'yı

2
@ TomášZato: Rastgele bir işaretçide sil komutunu çağırırsanız, o zaman berbatsınız demektir. Bunu yapmak için asla iyi bir neden yoktur. Aslında, akıllı işaretçi yıkıcıdan başka bir yerde el ile silme işlevini arıyorsanız, muhtemelen neden akıllı işaretçi veya başka bir nesne yöneticisi kullanmadığınıza ikinci bir göz atmak istersiniz.
Tutulma

shared_array sadece takviyeden, değil mi?
Dronz

30

New ile ayrılmış bir pointer üzerinde delete çağırdığınızda, işaret edilen nesnenin yıkıcısı çağrılacaktır.

A * p = new A;

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

22

Buna "yapısökücü" değil, "yıkıcı" adı verilir.

Her sınıfın yıkıcısının içinde, yeni ile ayrılmış tüm üye değişkenleri silmeniz gerekir.

düzenleme: Açıklığa kavuşturmak için:

Söyle var

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'nin bir örneğini atamak ve sonra silmek temizdir, çünkü B'nin dahili olarak ayırdığı şey yıkıcıda da silinir.

Ancak C sınıfı örnekler bellek sızdırır, çünkü serbest bırakmadığı bir A örneği tahsis eder (bu durumda C'nin bir yıkıcısı bile yoktur).


5

Eğer olağan bir pointer ( A*) varsa, açıkça yıkıcı içinde Aolmadıkça yıkıcı çağrılmaz (ve örneğin bellek de serbest bırakılmaz) . Otomatik imha istiyorsanız gibi akıllı işaretlere bakın .deleteBauto_ptr



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

Yaptığınızda:

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

Yıkıcı yalnızca temel sınıfınızda sanal anahtar kelime varsa çağrılır.

O zaman sanal bir yıkıcıya sahip olmasaydınız sadece ~ B () denirdi. Ancak sanal bir yıkıcıya sahip olduğunuz için önce ~ D () sonra da ~ B () denir.

Açık olarak silmediğiniz sürece öbek üzerinde tahsis edilen B veya D üyesi yer değiştirmez. Ve onları silmek onların yıkıcılarını da çağırır.


1

Senin gibi bir şeyin var

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

Daha sonra ararsanız delete b;, a'ya hiçbir şey olmaz ve bellek sızıntısı olur. Hatırlamaya çalışmak delete b->a;iyi bir çözüm değil, ama birkaç tane daha var.

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

Bu, B'yi silen bir yıkıcıdır. (A 0 ise, bu silme hiçbir şey yapmaz. A 0 değilse, ancak yeni bellekten belleğe işaret etmiyorsa, yığın bozulması oluşur.)

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

Bu şekilde, bir işaretçi olarak değil, bir otomatik_ptr <> (paylaşılan_ptr <> da veya diğer akıllı işaretçiler) yapar ve b olduğunda otomatik olarak silinir.

Bu yollardan her ikisi de iyi çalışıyor ve her ikisini de kullandım.


1

Sınıfımın yıkıcısının neden çağrılmadığını merak ediyordum. Bunun nedeni, o sınıfın tanımını (#include "class.h") eklemeyi unutmuş olmamdı. Sadece "A sınıfı" gibi bir deklarasyonum vardı; ve derleyici bundan memnun oldu ve "sil" diyeyim.


Derleyici uyarı düzeyini artırın
Phil1970

0

Hayır. İşaretçi silinir. Silme işlemini B'nin yıkıcısında açık bir şekilde A olarak adlandırmalısınız.


Bunu yapıyorum, sorum yıkıcıya mı çağrıldı?
Nick Bolton


0

hayır, A sınıfı için yıkıcı çağırmaz, açıkça (PoweRoy'un söylediği gibi), 'delete ptr;' karşılaştırmak için ...

  #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;
  }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.