Às vezes, noto programas que travam no meu computador com o erro: "chamada de função virtual pura".
Como esses programas compilam quando um objeto não pode ser criado de uma classe abstrata?
c++
polymorphism
virtual-functions
pure-virtual
Brian R. Bondy
fonte
fonte
doIt()
chamada no construtor é facilmente desvirtualizada e enviada paraBase::doIt()
estaticamente, o que apenas causa um erro de vinculador. O que realmente precisamos é uma situação em que o tipo dinâmico durante um despacho dinâmico seja o tipo de base abstrata.Base::Base
chamar um não virtualf()
que, por sua vez, chama odoIt
método virtual (puro) .Assim como o caso padrão de chamar uma função virtual do construtor ou destruidor de um objeto com funções virtuais puras, você também pode obter uma chamada de função virtual pura (pelo menos no MSVC) se chamar uma função virtual depois que o objeto foi destruído . Obviamente, isso é uma coisa muito ruim de se tentar, mas se você estiver trabalhando com classes abstratas como interfaces e bagunçar, então é algo que você pode ver. É possivelmente mais provável se você estiver usando interfaces de contagem referenciadas e tiver um bug de contagem de referência ou se tiver uma condição de corrida de uso / destruição de objeto em um programa multi-threaded ... O que há sobre esse tipo de chamada é que é muitas vezes é menos fácil entender o que está acontecendo, já que uma verificação dos 'suspeitos usuais' de chamadas virtuais em ctor e dtor aparecerá limpa.
Para ajudar a depurar esses tipos de problemas, você pode, em várias versões do MSVC, substituir o manipulador purecall da biblioteca de tempo de execução. Você faz isso fornecendo sua própria função com esta assinatura:
e vinculá-lo antes de vincular a biblioteca de tempo de execução. Isso dá a VOCÊ o controle do que acontece quando uma chamada direta é detectada. Depois de ter controle, você pode fazer algo mais útil do que o manipulador padrão. Eu tenho um manipulador que pode fornecer um rastreamento de pilha de onde a purecall aconteceu; veja aqui: http://www.lenholgate.com/blog/2006/01/purecall.html para mais detalhes.
(Observe que você também pode chamar _set_purecall_handler () para instalar seu manipulador em algumas versões do MSVC).
fonte
_purecall()
invocação que normalmente ocorre ao chamar um método de uma instância excluída não acontecerá se a classe base tiver sido declarada com a__declspec(novtable)
otimização (específico da Microsoft). Com isso, é inteiramente possível chamar um método virtual sobrescrito após o objeto ter sido excluído, o que poderia mascarar o problema até que ele o morda de alguma outra forma. A_purecall()
armadilha é sua amiga!Normalmente, quando você chama uma função virtual por meio de um ponteiro pendente - provavelmente a instância já foi destruída.
Também pode haver razões mais "criativas": talvez você tenha conseguido cortar a parte do seu objeto onde a função virtual foi implementada. Mas geralmente é só que a instância já foi destruída.
fonte
Corri para o cenário que as funções virtuais puras são chamadas por causa de objetos destruídos,
Len Holgate
já tenho uma resposta muito boa , gostaria de adicionar algumas cores com um exemplo:O destruidor da classe Derived redefine os pontos vptr para a classe vtable da base, que tem a função virtual pura, portanto, quando chamamos a função virtual, ele realmente chama as virutais puras.
Isso pode acontecer devido a um bug de código óbvio ou a um cenário complicado de condição de corrida em ambientes de multi-threading.
Aqui está um exemplo simples (compilação g ++ com otimização desativada - um programa simples pode ser facilmente otimizado):
E o rastreamento de pilha se parece com:
Realçar:
se o objeto for totalmente excluído, o que significa que o destruidor é chamado e o memroy é recuperado, podemos simplesmente obter um,
Segmentation fault
pois a memória voltou ao sistema operacional e o programa simplesmente não consegue acessá-lo. Portanto, esse cenário de "chamada de função virtual pura" geralmente ocorre quando o objeto é alocado no pool de memória, enquanto um objeto é excluído, a memória subjacente não é recuperada pelo SO, ela ainda está acessível ao processo.fonte
Eu acho que há um vtbl criado para a classe abstrata por algum motivo interno (pode ser necessário para algum tipo de informação de tipo de tempo de execução) e algo dá errado e um objeto real consegue. É um bug. Só isso já deveria dizer que algo que não pode acontecer é.
Especulação pura
editar: parece que estou errado no caso em questão. OTOH IIRC algumas linguagens permitem chamadas vtbl do destruidor do construtor.
fonte
Eu uso o VS2010 e sempre que tento chamar o destrutor diretamente do método público, recebo um erro de "chamada de função virtual pura" durante o tempo de execução.
Mudei o que está dentro de ~ Foo () para separar o método privado e funcionou perfeitamente.
fonte
Se você usa Borland / CodeGear / Embarcadero / Idera C ++ Builder, pode apenas implementar
Durante a depuração, coloque um ponto de interrupção no código e veja a pilha de chamadas no IDE, caso contrário, registre a pilha de chamadas em seu manipulador de exceções (ou essa função) se você tiver as ferramentas apropriadas para isso. Eu pessoalmente uso MadExcept para isso.
PS. A chamada de função original está em [C ++ Builder] \ source \ cpprtl \ Source \ misc \ pureerr.cpp
fonte
Aqui está uma maneira sorrateira de que isso aconteça. Isso essencialmente aconteceu comigo hoje.
fonte
I had this essentially happen to me today
obviamente não é verdade, porque simplesmente errado: uma função virtual pura é chamada apenas quandocallFoo()
é chamada dentro de um construtor (ou destruidor), porque neste momento o objeto ainda está (ou já) em um estágio. Aqui está uma versão em execução do seu código sem o erro de sintaxeB b();
- os parênteses a tornam uma declaração de função, você quer um objeto.