Quando são necessários destrutores virtuais em C++: análise em 2025

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Destrutores virtuais em C++: por que isso ainda é relevante (e quando pode ser ignorado)

\n\n

Se você já programou em C++ e lidou com herança, certamente já viu esta pergunta no Stack Overflow com dezenas de milhares de visualizações: «When to use virtual destructors?». Em 2025, o tema não perdeu relevância — vazamentos de memória devido a um virtual esquecido continuam causando estragos em produção. Mas há também o outro lado: adicionar virtual cegamente em todos os lugares possíveis também não é uma boa prática.

\n\n

Vamos entender de uma vez por todas: quando o destrutor virtual é obrigatório, quando é apenas uma boa prática, e quando sua presença indica uma arquitetura ruim. Vamos nessa.

\n\n

Resposta rápida (para quem está com pressa)

\n\n

O destrutor virtual é necessário se, e somente se, você pretende excluir um objeto de uma classe derivada através de um ponteiro para a classe base. Se esse cenário não ocorre no código — virtual não é necessário. Mas existem nuances que abordaremos a seguir.

\n\n

Por que isso é um problema? Anatomia de um vazamento

\n\n

Vejamos o antipadrão clássico que quebra o código de iniciantes (e não só).

\n\n
#include <iostream>\n\nclass Base {\npublic:\n    ~Base() { \n        std::cout << "Base destructor\\n"; \n    }\n};\n\nclass Derived : public Base {\nprivate:\n    int* big_data = new int[1000]; // recursos que precisam ser liberados\npublic:\n    Derived() { std::cout << "Derived created\\n"; }\n    ~Derived() {\n        delete[] big_data;\n        std::cout << "Derived destructor\\n";\n    }\n};\n\nint main() {\n    Base* ptr = new Derived();\n    delete ptr;  // Opa! Apenas ~Base() será chamado\n    return 0;\n}
\n\n

Saída:

\n
Derived created\nBase destructor
\n\n

O array big_data vazou. O destrutor de Derived não foi chamado porque o compilador, vendo o tipo Base*, chamou o destrutor exatamente dessa classe. E ele não é virtual.

\n\n

Solução — adicionar virtual antes do destrutor de Base:

\n\n
class Base {\npublic:\n    virtual ~Base() = default;  // ou corpo vazio\n};
\n\n

Agora, ao executar delete ptr, primeiro será executado ~Derived(), depois ~Base(). Os recursos são liberados, a memória não vaza, o analisador estático fica satisfeito.

\n\n

Quando o destrutor virtual é obrigatório (regra de ferro)

\n\n

Existe apenas um caso incondicional: se a classe for projetada para uso polimórfico através de ponteiro ou referência para a classe base, e os objetos das classes derivadas forem excluídos através desse ponteiro.

\n\n

Na prática, isso significa:

\n
    \n
  • A classe tem pelo menos uma função virtual (pura ou não)
  • \n
  • Você planeja usar Base* ou Base& para trabalhar com Derived
  • \n
  • Os objetos são criados no heap (new) e excluídos via delete
  • \n
\n\n

Exemplo — hierarquia clássica de formas:

\n\n
class Shape {\npublic:\n    virtual double area() const = 0;\n    virtual ~Shape() = default;  // obrigatório para classe abstrata\n};\n\nclass Circle : public Shape {\n    double radius;\npublic:\n    explicit Circle(double r) : radius(r) {}\n    double area() const override { return 3.14159 * radius * radius; }\n};\n\nint main() {\n    std::vector<std::unique_ptr<Shape>> shapes;\n    shapes.push_back(std::make_unique<Circle>(5.0));\n    // unique_ptr cuidará do delete, mas o destrutor de Shape deve ser virtual\n    return 0;\n}
\n\n

Observe: mesmo usando std::unique_ptr (que é a abordagem recomendada em 2025), o destrutor virtual ainda é necessário. O unique_ptr chama delete através do ponteiro para a classe base —

Blogs

Book Recommendations