aviso do compilador GNU “classe tem funções virtuais, mas destructor não-virtual”
Pergunta
defini uma interface em C ++, isto é, uma classe que contém funções virtuais única puros.
Eu quero proibir explicitamente os usuários da interface para excluir o objeto através de um ponteiro para a interface, então eu declarou um destruidor protegidos e não-virtual para a interface, algo como:
class ITest{
public:
virtual void doSomething() = 0;
protected:
~ITest(){}
};
void someFunction(ITest * test){
test->doSomething(); // ok
// deleting object is not allowed
// delete test;
}
O compilador GNU me dá um aviso dizendo:
classe 'ITest' tem funções virtuais, mas destructor não-virtual
Uma vez que o processo de destruição é protegido, qual é a diferença em ter ele virtual ou não-virtual?
Você acha que esse aviso pode ser ignorado ou silenciado com segurança?
Solução
É mais ou menos um erro no compilador. Note-se que em versões mais recentes do compilador este aviso não são jogados (pelo menos em 4.3 não faz). Tendo o destruidor de ser protegidos e não-virtual é completamente legítimo em seu caso.
aqui para um excelente artigo por Herb Sutter sobre o assunto. Do artigo:
Orientação n. 4: A classe base destrutor deve ser pública e virtual ou protegido e não virtual
Outras dicas
Alguns dos comentários sobre esta resposta relacionar com uma resposta antes que eu dei, o que estava errado.
A protegido meio destructor que só pode ser chamado de uma classe base, não através de exclusão. Isso significa que um ITest * não podem ser directamente eliminados, apenas uma classe derivada pode. A classe derivada pode muito bem querer um destrutor virtual. Não há nada de errado com seu código em tudo.
No entanto, desde que você não pode localmente desativar um aviso em GCC, e você já tem uma vtable, você poderia considerar apenas fazendo o de qualquer maneira virtual destructor. Vai custar-lhe 4 bytes para o programa (não por instância da classe), no máximo. Desde que você pode ter dado sua classe derivada um dtor virtual, você pode achar que não lhe custa nada.
Se você insistir em fazer isso, vá em frente e passar -Wno-non-virtual-dtor
a GCC. Este aviso não parece ser ativado por padrão, então você deve ter habilitado com -Wall
ou -Weffc++
. No entanto, eu acho que é um aviso útil, porque na maioria das situações isso seria um erro.
É uma classe de interface, por isso é razoável que você não deve objetos de exclusão implementar essa interface via essa interface. Um caso comum de que é uma interface para objetos criados por uma fábrica que deve ser devolvido à fábrica. (Tendo objetos contêm um ponteiro para sua fábrica pode ser muito caro).
Eu concordo com a observação de que o GCC está lamentando. Em vez disso, ele deve simplesmente avisar quando você exclui um * ITest. É aí que as mentiras de perigo real.
A minha opinião pessoal é que você está fazendo a coisa correta e o compilador está quebrado. Eu desativar o aviso (localmente no arquivo que define a interface) se possível,
Eu acho que eu uso este padrão (pequeno 'p') muito. Na verdade eu acho que é mais comum para os meus interfaces para ter dtors protegidos do que é para que eles tenham as públicas. No entanto, eu não acho que ele é realmente tão comum um idioma (ele não é falado sobre isso muito) e eu acho que para trás quando o aviso foi adicionado ao GCC era apropriado para tentar impor o 'dtor mais velhos devem ser virtual, se você tem regra funções virtuais. Pessoalmente eu atualizado que regra para 'dtor deve ser virtual, se você tem funções virtuais e usuários desejam ser capaz de excluir instâncias da interface através da interface então o dtor devem ser protegidos e não virtuais' há séculos;)
Se o destruidor é virtual que garante que o destruidor de classe base também é chamado de frente fazendo a limpeza, caso contrário, alguns vazamentos podem resultar de que o código. Portanto, você deve certificar-se de que o programa não tem tais advertências (prefferably há avisos em tudo).
Se você tivesse de código em um dos métodos de ITest
que tentaram delete
em si (uma má idéia, mas legal), destruidor da classe derivada não seria chamado. Você ainda deve fazer o seu processo de destruição virtual, mesmo se você nunca pretende excluir uma instância derivada através de um ponteiro de classe base.