Temel sanal yıkıcıyı açıkça çağırmam gerekir mi?


351

C ++ (sanal bir yıkıcı ile) bir sınıfı geçersiz kılarken yıkıcı sınıfı miras sınıfında tekrar sanal olarak uyguluyorum, ama temel yıkıcı çağırmak gerekir mi?

Eğer öyleyse bunun böyle bir şey hayal ediyorum ...

MyChildClass::~MyChildClass() // virtual in header
{
    // Call to base destructor...
    this->MyBaseClass::~MyBaseClass();

    // Some destructing specific to MyChildClass
}

Haklı mıyım?

Yanıtlar:


472

Hayır, yıkıcılar otomatik olarak ters sırayla çağrılır. (Temel sınıflar sürer). Temel sınıf yıkıcılarını çağırmayın.


Saf sanal yıkıcılar ne olacak? Bağlayıcım bunu, miras alınan sınıfın sanal olmayan yıkıcısının sonunda çağırmaya çalışıyor;
cjcurrie

40
beden olmadan saf bir sanal yıkıcıya sahip olamazsınız. Sadece boş bir beden verin. Düzenli bir saf sanal yöntemle, bunun yerine geçersiz kılma işlevi çağrılır, yıkıcılar ile hepsi çağrılır, bu nedenle bir gövde sağlamanız gerekir. = 0 sadece geçersiz kılınması gerektiği anlamına gelir, bu yüzden ihtiyacınız varsa yine de yararlı bir yapı.
Lou Franco

1
Bu soru ilişkili olabilir ve yardım soruları / 15265106 / ca-missing-vtable-error .
Paul-Sebastian Manole

Neden Nick Bolton'un kodu temel yıkıcıyı iki kez çağırmasına rağmen bir segmentasyon hatasına neden olmazken delete, temel sınıfa bir işaretçiyi iki kez çağırmak segmentasyon hatasına neden oluyor?
Maggyero

2
Yanlış kod içeren bir segmentasyon hatası garanti edilmez. Ayrıca, bir yıkıcı çağırmak bellek serbest bırakmaz.
Lou Franco

92

Hayır, temel yıkıcıyı çağırmanıza gerek yoktur, temel yıkıcı her zaman sizin için türetilmiş yıkıcı tarafından çağrılır. Lütfen imha sırası için ilgili cevabımı buraya bakınız .

Temel sınıfta neden sanal bir yıkıcı istediğinizi anlamak için lütfen aşağıdaki koda bakın:

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


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

Yaptığınızda:

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

Eğer B'de sanal bir yıkıcı yoksa, sadece ~ B () denirdi. Ancak sanal bir yıkıcıya sahip olduğunuz için önce ~ D () sonra da ~ B () denir.


20
Lütfen program (sözde) çıktısını ekleyin. okuyucuya yardımcı olacaktır.
Kuldeep Singh Dhaka

@KuldeepSinghDhaka Okuyucu bunu wandbox.org/permlink/KQtbZG1hjVgceSlO adresinde canlı olarak görebilir .
domuz

27

Diğerleri ne dedi, ama aynı zamanda türetilmiş sınıfta yıkıcı sanal ilan etmek zorunda olmadığını unutmayın. Bir temel yıkıcıyı sanal olarak bildirdiğinizde, temel sınıfta yaptığınız gibi, türetilmiş tüm yıkıcılar, siz bildirip bildirmeseniz de sanal olacaktır. Diğer bir deyişle:

struct A {
   virtual ~A() {}
};

struct B : public A {
   virtual ~B() {}   // this is virtual
};

struct C : public A {
   ~C() {}          // this is virtual too
};

1
~ B sanal ilan edilmezse ne olur? ~ C hala sanal mı?
Will

5
Evet. Sanal yöntem (yalnızca yıkıcı değil, herhangi biri) sanal olarak bildirildiğinde, türetilmiş sınıflardaki bu yöntemin tüm geçersiz kılmaları otomatik olarak sanal olur. Bu durumda, ~ B sanal bildirmeseniz bile, hala öyle ve ~ C de öyle.
boycy

1
Ancak, taban sınıfında karşılık gelen yöntemlerle aynı ada ve parametrelere sahip diğer geçersiz kılınmış yöntemlerin aksine, yıkıcı adı farklıdır. @boycy
Yuan Wen

1
@YuanWen hayır, (bir ve tek) türetilmiş yıkıcı her zaman temel sınıfın (bir ve tek) yıkıcısını geçersiz kılar.
boycy

10

Hayır. Çağrıyı 'zincirlemek' için açıkça Baseved yöntemini çağırdığınız diğer sanal yöntemlerin aksine, derleyici, yıkıcıları yapıcılarının çağrıldığı ters sırada çağırmak için kod üretir.


9

Hayır, asla temel sınıf yıkıcısı demezsiniz, her zaman diğerlerinin işaret ettiği gibi otomatik olarak çağrılır, ancak sonuçlarla birlikte kavramın kanıtı:

class base {
public:
    base()  { cout << __FUNCTION__ << endl; }
    ~base() { cout << __FUNCTION__ << endl; }
};

class derived : public base {
public:
    derived() { cout << __FUNCTION__ << endl; }
    ~derived() { cout << __FUNCTION__ << endl; } // adding call to base::~base() here results in double call to base destructor
};


int main()
{
    cout << "case 1, declared as local variable on stack" << endl << endl;
    {
        derived d1;
    }

    cout << endl << endl;

    cout << "case 2, created using new, assigned to derive class" << endl << endl;
    derived * d2 = new derived;
    delete d2;

    cout << endl << endl;

    cout << "case 3, created with new, assigned to base class" << endl << endl;
    base * d3 = new derived;
    delete d3;

    cout << endl;

    return 0;
}

Çıktı:

case 1, declared as local variable on stack

base::base
derived::derived
derived::~derived
base::~base


case 2, created using new, assigned to derive class

base::base
derived::derived
derived::~derived
base::~base


case 3, created with new, assigned to base class

base::base
derived::derived
base::~base

Press any key to continue . . .

Temel sınıf yıkıcıyı hangisi olması gerektiğini sanal olarak ayarlarsanız, durum 3 sonuçları durum 1 ve 2 ile aynı olur.


İyi örnek. Temel sınıf yıkıcısını türetilmiş sınıftan çağırmaya çalışırsanız, "error: 'BASE :: BASE ()' <newline> ~ BASE (); En azından bu benim g ++ 7.x derleyiciden gelen davranıştır.
Kemin Zhou


1

C ++ 'daki yıkıcılar, yalnızca Base sınıfı yıkıcı bildirildiğinde , yapılarına göre (Türetildikten sonra Taban) otomatik olarak çağrılır .virtual

Değilse, nesne silme sırasında yalnızca temel sınıf yıkıcı çağrılır.

Örnek: Sanal Yıkıcı Olmadan

#include <iostream>

using namespace std;

class Base{
public:
  Base(){
    cout << "Base Constructor \n";
  }

  ~Base(){
    cout << "Base Destructor \n";
  }

};

class Derived: public Base{
public:
  int *n;
  Derived(){
    cout << "Derived Constructor \n";
    n = new int(10);
  }

  void display(){
    cout<< "Value: "<< *n << endl;
  }

  ~Derived(){
    cout << "Derived Destructor \n";
  }
};

int main() {

 Base *obj = new Derived();  //Derived object with base pointer
 delete(obj);   //Deleting object
 return 0;

}

Çıktı

Base Constructor
Derived Constructor
Base Destructor

Örnek: Temel sanal Yıkıcı ile

#include <iostream>

using namespace std;

class Base{
public:
  Base(){
    cout << "Base Constructor \n";
  }

  //virtual destructor
  virtual ~Base(){
    cout << "Base Destructor \n";
  }

};

class Derived: public Base{
public:
  int *n;
  Derived(){
    cout << "Derived Constructor \n";
    n = new int(10);
  }

  void display(){
    cout<< "Value: "<< *n << endl;
  }

  ~Derived(){
    cout << "Derived Destructor \n";
    delete(n);  //deleting the memory used by pointer
  }
};

int main() {

 Base *obj = new Derived();  //Derived object with base pointer
 delete(obj);   //Deleting object
 return 0;

}

Çıktı

Base Constructor
Derived Constructor
Derived Destructor
Base Destructor

Temel sınıf yıkıcısının virtualaksi halde bildirilmesi önerilir , tanımlanmamış davranışa neden olur.

Referans: Sanal Yıkıcı

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.