virtuais Por que devo declarar um destruidor virtual para uma classe abstrata em C++?




programação orientada a objetos c++ exercicios resolvidos (6)

A resposta é simples, você precisa ser virtual, caso contrário a classe base não seria uma classe polimórfica completa.

    Base *ptr = new Derived();
    delete ptr; // Here the call order of destructors: first Derived then Base.

Você preferiria a exclusão acima, mas se o destruidor da classe base não for virtual, somente o destrutor da classe base será chamado e todos os dados na classe derivada permanecerão excluídos.

Eu sei que é uma boa prática declarar destruidores virtuais para classes base em C ++, mas é sempre importante declarar destruidores virtual mesmo para classes abstratas que funcionam como interfaces? Por favor, forneça algumas razões e exemplos do porquê.


A resposta para sua pergunta é frequentemente, mas nem sempre. Se a sua classe abstrata proibir os clientes de chamarem delete em um ponteiro para ela (ou se ela diz isso em sua documentação), você está livre para não declarar um destruidor virtual.

Você pode proibir os clientes de chamar delete em um ponteiro para ele, tornando seu destrutor protegido. Trabalhando assim, é perfeitamente seguro e razoável omitir um destruidor virtual.

Você acabará não tendo uma tabela de métodos virtual e acabará sinalizando aos seus clientes a intenção de torná-los não excluíveis por meio de um ponteiro para eles. Portanto, você tem motivos para não declará-los virtuais nesses casos.

[Veja o item 4 deste artigo: http://www.gotw.ca/publications/mill18.htm ]


Nem sempre é necessário, mas acho que é uma boa prática. O que ele faz é permitir que um objeto derivado seja excluído com segurança através de um ponteiro de um tipo base.

Então, por exemplo:

Base *p = new Derived;
// use p as you see fit
delete p;

é malformado se o Base não tiver um destruidor virtual, porque ele tentará excluir o objeto como se fosse uma Base * .


É ainda mais importante para uma interface. Qualquer usuário de sua classe provavelmente terá um ponteiro para a interface, não um ponteiro para a implementação concreta. Quando eles vêm para excluí-lo, se o destruidor não for virtual, eles chamarão o destruidor da interface (ou o padrão fornecido pelo compilador, se você não especificou um), não o destrutor da classe derivada. Vazamento de memória instantânea.

Por exemplo

class Interface
{
   virtual void doSomething() = 0;
};

class Derived : public Interface
{
   Derived();
   ~Derived() 
   {
      // Do some important cleanup...
   }
};

void myFunc(void)
{
   Interface* p = new Derived();
   // The behaviour of the next line is undefined. It probably 
   // calls Interface::~Interface, not Derived::~Derived
   delete p; 
}

Não é só uma boa prática. É a regra nº 1 para qualquer hierarquia de classes.

  1. A base mais classe de uma hierarquia em C ++ deve ter um destruidor virtual

Agora para o porquê. Pegue a hierarquia animal típica. Os destruidores virtuais passam pelo despacho virtual exatamente como qualquer outra chamada de método. Tome o seguinte exemplo.

Animal* pAnimal = GetAnimal();
delete pAnimal;

Suponha que Animal seja uma classe abstrata. A única maneira que o C ++ sabe o destruidor apropriado para chamar é via envio de método virtual. Se o destruidor não for virtual, ele simplesmente chamará o destruidor do Animal e não destruirá nenhum objeto nas classes derivadas.

A razão para tornar o destruidor virtual na classe base é que ele simplesmente remove a escolha de classes derivadas. Seu destruidor se torna virtual por padrão.


Sim, é sempre importante. As classes derivadas podem alocar memória ou manter referência a outros recursos que precisarão ser limpos quando o objeto for destruído. Se você não der a seus destruidores virtuais de interfaces / classes abstratas, toda vez que você excluir uma instância de classe derivada por meio de uma classe base manipuladora, o destruidor de sua classe derivada não será chamado.

Portanto, você está abrindo o potencial para vazamentos de memória

class IFoo
{
  public:
    virtual void DoFoo() = 0;
};

class Bar : public IFoo
{
  char* dooby = NULL;
  public:
    virtual void DoFoo() { dooby = new char[10]; }
    void ~Bar() { delete [] dooby; }
};

IFoo* baz = new Bar();
baz->DoFoo();
delete baz; // memory leak - dooby isn't deleted




virtual-destructor