virtuais - programação orientada a objetos c++ exercicios resolvidos




Por que devo declarar um destruidor virtual para uma classe abstrata em C++? (5)

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


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

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 decidi fazer alguma pesquisa e tentar resumir suas respostas. As perguntas a seguir ajudarão você a decidir que tipo de destruidor você precisa:

  1. Sua aula pretende ser usada como uma classe base?
    • Não: Declare o destruidor não virtual público para evitar v-pointer em cada objeto da classe * .
    • Sim: Leia a próxima pergunta.
  2. Sua classe base é abstrata? (ou seja, qualquer método virtual puro?)
    • Não: tente transformar o resumo da sua classe base redesenhando sua hierarquia de classes
    • Sim: Leia a próxima pergunta.
  3. Você deseja permitir a exclusão polimórfica por meio de um ponteiro base?
    • Não: declare o destrutor virtual protegido para evitar o uso indesejado.
    • Sim: declarar o destruidor virtual público (sem sobrecarga nesse caso).

Eu espero que isso ajude.

* É importante notar que não há nenhuma maneira em C ++ para marcar uma classe como final (ou seja, não subclassível), então no caso em que você decide declarar seu destrutivo não-virtual e público, lembre-se de avisar explicitamente seus colegas programadores contra derivando de sua classe.

Referências:


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