Pergunta

Às vezes, programas Aviso que batem no meu computador com o erro:. "Chamada de função virtual pura"

Como é que estes programas ainda compilar quando um objeto não pode ser criado de uma classe abstrata?

Foi útil?

Solução

Eles podem resultar se você tentar fazer uma chamada de função virtual a partir de um construtor ou destruidor. Desde que você não pode fazer uma chamada de função virtual a partir de um construtor ou destrutor (o objeto classe derivada não foi construído ou já foi destruída), ele chama a versão da classe base, que no caso de uma função virtual pura, doesn 't exist.

(Veja ao vivo demonstração aqui )

class Base
{
public:
    Base() { doIt(); }  // DON'T DO THIS
    virtual void doIt() = 0;
};

void Base::doIt()
{
    std::cout<<"Is it fine to call pure virtual function from constructor?";
}

class Derived : public Base
{
    void doIt() {}
};

int main(void)
{
    Derived d;  // This will cause "pure virtual function call" error
}

Outras dicas

Assim como o caso padrão de chamar uma função virtual a partir do construtor ou destrutor de um objeto com funções virtuais puras você também pode obter uma chamada de função virtual pura (em MSVC pelo menos) se você chamar uma função virtual depois que o objeto foi destruída. Obviamente, isso é uma coisa muito ruim para tentar fazer, mas se você está trabalhando com classes abstratas como interfaces e você fracassar, então é algo que você pode ver. É possivelmente mais provável se você estiver usando interfaces referenciadas contados e você tem um bug contagem ref ou se você tiver uma condição de destruição raça uso de objeto / objeto em um programa multi-threaded ... A coisa sobre esses tipos de purecall é que é muitas vezes, menos fácil de entender o que está acontecendo como um cheque para os 'suspeitos do costume' de chamadas virtuais em ctor e dtor vai aparecer limpo.

Ajuda para com a depuração esses tipos de problemas que você pode, em várias versões do MSVC, substituir manipulador purecall da biblioteca de tempo de execução. Você faz isso fornecendo sua própria função com essa assinatura:

int __cdecl _purecall(void)

e vinculá-lo antes de ligar a biblioteca de tempo de execução. Isto dá-lhe o controle do que acontece quando um purecall é detectado. Uma vez que você tem o 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 o purecall aconteceu; veja aqui: http://www.lenholgate.com/blog/2006/01/ purecall.html para mais detalhes.

(Nota você pode também chamar _set_purecall_handler () para instalar o manipulador em algumas versões do MSVC).

Normalmente, quando você chamar uma função virtual através de um ponteiro pendurado -. Provavelmente a instância já foi destruída

Pode haver mais razões "criativas", também: talvez você conseguiu fatia fora a parte do seu objeto onde a função virtual foi implementado. Mas, geralmente, é só que a instância já foi destruída.

Eu acho que há uma vtbl criado para a classe abstrata por alguma razão interna (pode ser necessário para algum tipo de tempo de execução digite info) e algo dá errado e um objeto real recebe-lo. É um bug. Isso por si só deveria dizer que algo que não pode acontecer é.

Pura especulação

Editar: parece que eu estou errado, no caso em questão. OTOH IIRC algumas línguas permitem fazer chamadas VTBL fora do destructor construtor.

Eu uso VS2010 e sempre que eu tente chamar destrutor diretamente do método público, eu recebo um erro de "pura chamada de função virtual" durante a execução.

template <typename T>
class Foo {
public:
  Foo<T>() {};
  ~Foo<T>() {};

public:
  void SomeMethod1() { this->~Foo(); }; /* ERROR */
};

Então me mudei o que está dentro ~ Foo () para separar método privado, em seguida, ele trabalhou como um encanto.

template <typename T>
class Foo {
public:
  Foo<T>() {};
  ~Foo<T>() {};

public:
  void _MethodThatDestructs() {};
  void SomeMethod1() { this->_MethodThatDestructs(); }; /* OK */
};

Se você usar Borland / CodeGear / Embarcadero / Idera C ++ Builder, o seu pode simplesmente implementar

extern "C" void _RTLENTRY _pure_error_()
{
    //_ErrorExit("Pure virtual function called");
    throw Exception("Pure virtual function called");
}

Durante a depuração lugar um ponto de interrupção no código e ver a pilha de chamadas no IDE, caso contrário, registrar a pilha de chamadas no seu manipulador de exceção (ou aquela função) se você tiver as ferramentas adequadas 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

Eu corri para o cenário que as funções virtuais puras é chamada por causa de objetos destruídos, Len Holgate já tem uma resposta muito bom, eu gostaria para adicionar um pouco de cor com um exemplo:

  1. Um objecto Derivado é criado, e o ponteiro (como Classe base) está em algum lugar salvo
  2. O objeto Derived é excluído, mas de alguma forma o ponteiro está ainda referenciada
  3. O ponteiro que aponta para apagado Derivado objeto é chamado

O destruidor de classe derivada redefinir os pontos vptr para o vtable classe Base, que tem a função virtual pura, por isso, quando nós chamamos a função virtual, ele realmente põe nos virutal puros.

Isso pode acontecer por causa de um erro de código óbvio, ou um cenário complicado de condição de corrida em ambientes multi-threading.

Aqui está um exemplo simples (g ++ compilação com otimização desligado - um programa simples pode ser facilmente otimizado de distância):

 #include <iostream>
 using namespace std;

 char pool[256];

 struct Base
 {
     virtual void foo() = 0;
     virtual ~Base(){};
 };

 struct Derived: public Base
 {
     virtual void foo() override { cout <<"Derived::foo()" << endl;}
 };

 int main()
 {
     auto* pd = new (pool) Derived();
     Base* pb = pd;
     pd->~Derived();
     pb->foo();
 }

e os olhares de rastreamento de pilha como:

#0  0x00007ffff7499428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007ffff749b02a in __GI_abort () at abort.c:89
#2  0x00007ffff7ad78f7 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7adda46 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007ffff7adda81 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007ffff7ade84f in __cxa_pure_virtual () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x0000000000400f82 in main () at purev.C:22

Destaque:

Se o objeto é totalmente excluído, o que significa destruidor é chamado, e memroy se recuperado, podemos simplesmente ter uma Segmentation fault como a memória voltou a ser o sistema operacional e o programa simplesmente não pode acessá-lo. Portanto, este cenário "pura chamada de função virtual" geralmente acontece quando o objeto é alocado no pool de memória, enquanto um objeto é excluído, a memória subjacente realmente não é recuperado pelo sistema operacional, ele ainda está lá acessível pelo processo.

Aqui está uma maneira sorrateira para que isso aconteça. Eu tinha esta essencialmente acontecer comigo hoje.

class A
{
  A *pThis;
  public:
  A()
   : pThis(this)
  {
  }

  void callFoo()
  {
    pThis->foo(); // call through the pThis ptr which was initialized in the constructor
  }

  virtual void foo() = 0;
};

class B : public A
{
public:
  virtual void foo()
  {
  }
};

B b();
b.callFoo();
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top