Devo chiamare esplicitamente il distruttore virtuale di base?


350

Quando eseguo l'override di una classe in C ++ (con un distruttore virtuale) sto implementando nuovamente il distruttore come virtuale sulla classe ereditaria, ma devo chiamare il distruttore di base?

Se è così, immagino che sia qualcosa del genere ...

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

    // Some destructing specific to MyChildClass
}

Ho ragione?

Risposte:


469

No, i distruttori vengono chiamati automaticamente nell'ordine inverso rispetto alla costruzione. (Le classi di base sono le ultime). Non chiamare distruttori della classe base.


Che dire dei puri distruttori virtuali? Il mio linker sta cercando di chiamarlo alla fine del distruttore non virtuale della mia classe ereditata;
cjcurrie,

40
non puoi avere un puro distruttore virtuale senza un corpo. Dagli solo un corpo vuoto. Con un normale metodo virtuale puro, viene invece chiamata la funzione di override, con i distruttori, vengono tutti chiamati, quindi è necessario fornire un corpo. = 0 significa solo che deve essere sovrascritto, quindi comunque un costrutto utile se ne hai bisogno.
Lou Franco,

1
Questa domanda potrebbe essere correlata e aiutare domande / 15265106 / ca-missing-vtable-error .
Paul-Sebastian Manole,

Perché il codice di Nick Bolton non causa un errore di segmentazione anche se chiama due volte il distruttore di base, mentre chiamare due volte deleteun puntatore alla classe base provoca un errore di segmentazione?
Maggyero,

2
Non è garantito un errore di segmentazione con codice errato. Inoltre, chiamare un distruttore non rilascia memoria.
Lou Franco,

92

No, non è necessario chiamare il distruttore di base, un distruttore di base viene sempre chiamato per te dal distruttore derivato. Si prega di consultare la mia risposta correlata qui per ordine di distruzione .

Per capire perché desideri un distruttore virtuale nella classe base, consulta il codice seguente:

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


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

Quando lo fai:

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

Quindi se non avessi un distruttore virtuale in B, verrebbe chiamato solo ~ B (). Ma poiché hai un distruttore virtuale, prima verrà chiamato ~ D (), quindi ~ B ().


20
Includere l'output del programma (pseudo). aiuterà il lettore.
Kuldeep Singh Dhaka,

@KuldeepSinghDhaka Il lettore può vederlo dal vivo su wandbox.org/permlink/KQtbZG1hjVgceSlO .
il maiale il

27

Quello che hanno detto gli altri, ma nota anche che non devi dichiarare il distruttore virtuale nella classe derivata. Una volta dichiarato virtuale un distruttore, come nella classe base, tutti i distruttori derivati ​​saranno virtuali, indipendentemente dal fatto che lo dichiariate o meno. In altre parole:

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

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

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

1
cosa succede se ~ B non viene dichiarato virtuale? ~ C è ancora virtuale?
Sarà il

5
Sì. Quando un metodo virtuale (qualsiasi, non solo il distruttore) viene dichiarato virtuale, tutte le sostituzioni di quel metodo nelle classi derivate sono automaticamente virtuali. In questo caso, anche se non dichiari ~ B virtuale, lo è ancora, e così è ~ C.
boycy

1
Ma a differenza di altri metodi sostituiti con lo stesso nome e parametri dei metodi corrispondenti nella classe base, il nome del distruttore è diverso. @boycy
Yuan Wen,

1
@YuanWen no, il (unico e solo) distruttore derivato ha sempre la precedenza sul (solo e unico) distruttore della sua classe base.
Boycy,

10

No. A differenza di altri metodi virtuali, in cui si chiamerebbe esplicitamente il metodo Base dal derivato per "concatenare" la chiamata, il compilatore genera codice per chiamare i distruttori nell'ordine inverso in cui sono stati chiamati i loro costruttori.


9

No, non si chiama mai il distruttore della classe base, viene sempre chiamato automaticamente come altri hanno sottolineato, ma qui è la prova del concetto con i risultati:

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

L'output è:

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 . . .

Se imposti il ​​distruttore della classe base come virtuale quale dovrebbe essere, i risultati del caso 3 sarebbero gli stessi dei casi 1 e 2.


Buona illustrazione Se provi a chiamare il distruttore della classe base dalla classe derivata dovresti ottenere un errore del compilatore simile a "errore: nessuna funzione corrispondente per la chiamata a 'BASE :: BASE ()' <newline> ~ BASE ();" Almeno questo è il comportamento del mio compilatore g ++ 7.x.
Kemin Zhou


1

I distruttori in C ++ vengono chiamati automaticamente nell'ordine delle loro costruzioni (Derivato poi Base) solo quando viene dichiarato il distruttore della classe Basevirtual .

In caso contrario, al momento dell'eliminazione dell'oggetto viene invocato solo il distruttore della classe base.

Esempio: senza distruttore virtuale

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

}

Produzione

Base Constructor
Derived Constructor
Base Destructor

Esempio: con base Virtual Destructor

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

}

Produzione

Base Constructor
Derived Constructor
Derived Destructor
Base Destructor

Si consiglia di dichiarare il distruttore della classe base in caso virtualcontrario, causa un comportamento indefinito.

Riferimento: Virtual Destructor

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.