Dois-je appeler explicitement le destructeur virtuel de base?

350

Lorsque je remplace une classe en C ++ (avec un destructeur virtuel), j'implémente à nouveau le destructeur en tant que virtuel sur la classe héritée, mais dois-je appeler le destructeur de base?

Si c'est le cas, j'imagine que c'est quelque chose comme ça ...

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

    // Some destructing specific to MyChildClass
}

Ai-je raison?

Nick Bolton
la source

Réponses:

471

Non, les destructeurs sont appelés automatiquement dans l'ordre inverse de la construction. (Les classes de base durent). N'appelez pas de destructeurs de classe de base.

Lou Franco
la source
Qu'en est-il des destructeurs virtuels purs? Mon éditeur de liens essaie de l'appeler à la fin du destructeur non virtuel de ma classe héritée;
cjcurrie
40
vous ne pouvez pas avoir un destructeur virtuel pur sans corps. Donnez-lui simplement un corps vide. Avec une méthode virtuelle pure régulière, la fonction prioritaire est appelée à la place, avec les destructeurs, ils sont tous appelés, vous devez donc fournir un corps. Le = 0 signifie simplement qu'il doit être remplacé, donc toujours une construction utile si vous en avez besoin.
Lou Franco
1
Cette question peut être liée et aider les questions / 15265106 / ca-missing-vtable-error .
Paul-Sebastian Manole
Pourquoi le code de Nick Bolton ne provoque-t-il pas une erreur de segmentation bien qu'il appelle deux fois le destructeur de base, alors que l'appel deleteà un pointeur vers la classe de base deux fois provoque une erreur de segmentation?
Maggyero
2
Vous n'êtes pas assuré d'un défaut de segmentation avec un mauvais code. De plus, appeler un destructeur ne libère pas de mémoire.
Lou Franco
92

Non, vous n'avez pas besoin d'appeler le destructeur de base, un destructeur de base est toujours appelé pour vous par le destructeur dérivé. Veuillez voir ma réponse connexe ici pour l'ordre de destruction .

Pour comprendre pourquoi vous voulez un destructeur virtuel dans la classe de base, veuillez consulter le code ci-dessous:

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


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

Quand vous faites:

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

Ensuite, si vous n'aviez pas de destructeur virtuel dans B, seul ~ B () serait appelé. Mais puisque vous avez un destructeur virtuel, d'abord ~ ​​D () sera appelé, puis ~ B ().

Brian R. Bondy
la source
20
Veuillez inclure la sortie du programme (pseudo). cela aidera le lecteur.
Kuldeep Singh Dhaka
@KuldeepSinghDhaka Le lecteur peut le voir en direct sur wandbox.org/permlink/KQtbZG1hjVgceSlO .
le porc
27

Ce que les autres ont dit, mais notez également que vous n'avez pas à déclarer le destructeur virtuel dans la classe dérivée. Une fois que vous déclarez un destructeur virtuel, comme vous le faites dans la classe de base, tous les destructeurs dérivés seront virtuels, que vous le déclariez ou non. En d'autres termes:

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

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

struct C : public A {
   ~C() {}          // this is virtual too
};
Wodzu
la source
1
que faire si ~ B n'est pas déclaré virtuel? ~ C est-il toujours virtuel?
Will
5
Oui. Lorsqu'une méthode virtuelle (n'importe laquelle, pas seulement le destructeur) est déclarée virtuelle, tous les remplacements de cette méthode dans les classes dérivées sont automatiquement virtuels. Dans ce cas, même si vous ne déclarez pas ~ B virtuel, il l'est toujours, tout comme ~ C.
boycy
1
Mais contrairement à d'autres méthodes remplacées ayant le même nom et les mêmes paramètres que leurs méthodes correspondantes dans la classe de base, le nom du destructeur est différent. @boycy
Yuan Wen
1
@YuanWen non, le destructeur dérivé (un et seul) remplace toujours le destructeur (unique et unique) de sa classe de base.
boycy
10

Non. Contrairement à d'autres méthodes virtuelles, où vous appelez explicitement la méthode Base à partir de Derived pour «chaîner» l'appel, le compilateur génère du code pour appeler les destructeurs dans l'ordre inverse dans lequel leurs constructeurs ont été appelés.

itsmatt
la source
9

Non, vous n'appelez jamais le destructeur de classe de base, il est toujours appelé automatiquement comme d'autres l'ont souligné, mais voici une preuve de concept avec des résultats:

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

La sortie est:

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

Si vous définissez le destructeur de classe de base comme virtuel, ce qui devrait être le cas, les résultats du cas 3 seraient les mêmes que les cas 1 et 2.

zar
la source
Bonne illustration. Si vous essayez d'appeler le destructeur de classe de base à partir de la classe dérivée, vous devriez obtenir une erreur de compilation similaire à "erreur: aucune fonction correspondante pour l'appel à 'BASE :: BASE ()' <newline> ~ BASE ();" C'est du moins le comportement de mon compilateur g ++ 7.x.
Kemin Zhou
6

Non, il est automatiquement appelé.

Benoît
la source
1

Les destructeurs en C ++ sont automatiquement appelés dans l'ordre de leurs constructions (dérivé puis base) uniquement lorsque le destructeur de classe Base est déclarévirtual .

Sinon, seul le destructeur de classe de base est invoqué au moment de la suppression de l'objet.

Exemple: sans destructeur virtuel

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

}

Production

Base Constructor
Derived Constructor
Base Destructor

Exemple: avec le destructeur virtuel de base

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

}

Production

Base Constructor
Derived Constructor
Derived Destructor
Base Destructor

Il est recommandé de déclarer le destructeur de classe de base car virtualsinon, cela provoque un comportement indéfini.

Référence: Virtual Destructor

Adarsh ​​Kumar
la source