Preciso chamar explicitamente o destruidor virtual básico?

350

Ao substituir uma classe em C ++ (com um destruidor virtual), estou implementando o destruidor novamente como virtual na classe herdada, mas preciso chamar o destruidor base?

Se sim, imagino que seja algo assim ...

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

    // Some destructing specific to MyChildClass
}

Estou certo?

Nick Bolton
fonte

Respostas:

469

Não, os destruidores são chamados automaticamente na ordem inversa da construção. (Classes base por último). Não chame destruidores da classe base.

Lou Franco
fonte
E os destruidores virtuais puros? Meu vinculador está tentando chamá-lo no final do destruidor não virtual da minha classe herdada;
Cjcurrie
40
você não pode ter um destruidor virtual puro sem um corpo. Apenas dê a ele um corpo vazio. Com um método virtual puro regular, a função de substituição é chamada, em vez de destruidores, todos são chamados, então você precisa fornecer um corpo. O = 0 significa apenas que ele deve ser substituído, portanto, ainda é uma construção útil, se você precisar.
Lou Franco
11
Esta pergunta pode estar relacionada e perguntas de ajuda / 15265106 / ca-missing-vtable-error .
Paul-Sebastian Manole
Por que o código de Nick Bolton não causa uma falha de segmentação, embora chame o destruidor de base duas vezes, enquanto chamar deleteum ponteiro para a classe base duas vezes causa uma falha de segmentação?
Maggyero
2
Não é garantida uma falha de segmentação com nenhum código errado. Além disso, chamar um destruidor não libera memória.
Lou Franco
92

Não, você não precisa chamar o destruidor de base, um destruidor de base é sempre chamado pelo destruidor derivado. Por favor, veja minha resposta relacionada aqui para ordem de destruição .

Para entender por que você deseja um destruidor virtual na classe base, consulte o código abaixo:

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


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

Quando você faz:

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

Então, se você não tivesse um destruidor virtual em B, apenas ~ B () seria chamado. Mas como você tem um destruidor virtual, primeiro ~ D () será chamado, depois ~ B ().

Brian R. Bondy
fonte
20
Por favor inclua a saída do programa (pseudo). isso ajudará o leitor.
Kuldeep Singh Dhaka
@KuldeepSinghDhaka O leitor pode vê-lo ao vivo em wandbox.org/permlink/KQtbZG1hjVgceSlO .
the suine
27

O que os outros disseram, mas também observe que você não precisa declarar o destruidor virtual na classe derivada. Depois de declarar um destruidor virtual, como você faz na classe base, todos os destruidores derivados serão virtuais, independentemente de você o declarar ou não. Em outras palavras:

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

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

struct C : public A {
   ~C() {}          // this is virtual too
};
Wodzu
fonte
11
e se ~ B não for declarado virtual? ~ C ainda é virtual?
Will
5
Sim. Quando um método virtual (qualquer um, não apenas o destruidor) é declarado virtual, todas as substituições desse método nas classes derivadas são automaticamente virtuais. Nesse caso, mesmo se você não declarar ~ B virtual, ainda é, e também é ~ C.
boycy
11
Mas, diferentemente de outros métodos substituídos com o mesmo nome e parâmetros de seus métodos correspondentes na classe base, o nome do destruidor é diferente. @boycy
Yuan Wen
11
@YuanWen, não, o destruidor (único) derivado sempre substitui (o único) destruidor da classe base.
boycy
10

Não. Diferente de outros métodos virtuais, nos quais você explicitamente chamaria o método Base da derivada para 'encadear' a chamada, o compilador gera código para chamar os destruidores na ordem inversa na qual seus construtores foram chamados.

itsmatt
fonte
9

Não, você nunca chama o destruidor da classe base, ele sempre é chamado automaticamente como outros já apontaram, mas aqui está a prova de conceito com resultados:

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

A saída é:

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 você definir o destruidor da classe base como virtual, qual deve ser o resultado do caso 3, será o mesmo do caso 1 e 2.

zar
fonte
Boa ilustração Se você tentar chamar o destruidor da classe base a partir da classe derivada, deverá receber um erro do compilador semelhante a "error: nenhuma função correspondente à chamada para 'BASE :: BASE ()' <newline> ~ BASE ();" Pelo menos esse é o comportamento do meu compilador g ++ 7.x.
Kemin Zhou
6

Não. É chamado automaticamente.

Benoît
fonte
1

Os destruidores no C ++ são chamados automaticamente na ordem de suas construções (Derivado e Base) somente quando o destruidor da classe Base é declaradovirtual .

Caso contrário, somente o destruidor da classe base será chamado no momento da exclusão do objeto.

Exemplo: sem destruidor virtual

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

}

Resultado

Base Constructor
Derived Constructor
Base Destructor

Exemplo: Com o Destructor virtual básico

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

}

Resultado

Base Constructor
Derived Constructor
Derived Destructor
Base Destructor

Recomenda-se declarar o destruidor da classe base, virtualcaso contrário, isso causa um comportamento indefinido.

Referência: Destructor Virtual

Adarsh ​​Kumar
fonte