Da dove vengono gli arresti di “pura chiamata a una funzione virtuale”?

A volte noto dei programmi che si bloccano sul mio computer con l’errore: “chiamata di funzione virtuale pura”.

In che modo questi programmi vengono compilati anche quando non è ansible creare un object di una class astratta?

Possono risultare se si tenta di effettuare una chiamata di funzione virtuale da un costruttore o un distruttore. Poiché non è ansible effettuare una chiamata di funzione virtuale da un costruttore o un distruttore (l’object di class derivata non è stato costruito o è già stato distrutto), chiama la versione della class base, che nel caso di una funzione virtuale pura, non esegue esiste

(Vedi demo dal vivo qui )

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 } 

Oltre al caso standard di chiamare una funzione virtuale dal costruttore o dal distruttore di un object con funzioni virtuali pure, è ansible ottenere anche una chiamata di funzione virtuale pura (almeno su MSVC) se si chiama una funzione virtuale dopo che l’object è stato distrutto . Ovviamente questa è una cosa piuttosto negativa da provare e fare, ma se si lavora con classi astratte come interfacce e si incasina, è qualcosa che si potrebbe vedere. Probabilmente è più probabile che tu stia usando le interfacce conteggiate referenziate e che tu abbia un errore di conteggio ref o se tu abbia una condizione di uso / distruzione dell’object di gara in un programma multi-thread … La cosa su questi tipi di purecall è che è spesso meno facile capire cosa sta succedendo come controllo per i “soliti sospetti” delle chiamate virtuali in Ctor e Dtor verrà pulito.

Per facilitare il debug di questo tipo di problemi, in varie versioni di MSVC è ansible sostituire il gestore purecall della libreria di runtime. Lo fai fornendo la tua funzione con questa firma:

 int __cdecl _purecall(void) 

e collegandolo prima di colbind la libreria di runtime. Questo ti dà il controllo di ciò che accade quando viene rilevata una chiamata completa. Una volta che hai il controllo puoi fare qualcosa di più utile del gestore standard. Ho un gestore che può fornire una traccia dello stack di dove è avvenuta la purecall; vedere qui: http://www.lenholgate.com/blog/2006/01/purecall.html per ulteriori dettagli.

(Nota puoi anche chiamare _set_purecall_handler () per installare il tuo gestore in alcune versioni di MSVC).

Di solito quando si chiama una funzione virtuale attraverso un puntatore pendente, molto probabilmente l’istanza è già stata distrutta.

Possono esserci anche altri motivi “creativi”: forse sei riuscito a tagliare la parte dell’object dove è stata implementata la funzione virtuale. Ma di solito è solo che l’istanza è già stata distrutta.

Immagino che ci sia un vtbl creato per la class astratta per qualche motivo interno (potrebbe essere necessario per qualche tipo di informazioni sul tipo di runtime) e qualcosa va storto e un object reale lo ottiene. È un bug Solo questo dovrebbe dire che qualcosa che non può accadere è.

Pura speculazione

modifica: sembra che io abbia torto nel caso in questione. OTOH IIRC alcune lingue consentono chiamate vtbl al distruttore del costruttore.

Io uso VS2010 e ogni volta che provo a chiamare il distruttore direttamente dal metodo pubblico, ottengo un errore di “pura chiamata di funzione virtuale” durante il runtime.

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

Così ho spostato ciò che c’è dentro ~ Foo () per separare il metodo privato, poi ha funzionato come un incantesimo.

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

Se usi Borland / CodeGear / Embarcadero / Idera C ++ Builder, puoi implementarlo

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

Durante il debug, inserire un punto di interruzione nel codice e vedere il callstack nell’IDE, altrimenti registrare lo stack di chiamate nel gestore di eccezioni (o quella funzione) se si dispone degli strumenti appropriati. Personalmente uso MadExcept per questo.

PS. La chiamata della funzione originale è in [C ++ Builder] \ source \ cpprtl \ Source \ misc \ pureerr.cpp

Ecco un modo subdolo perché ciò accada. Questo mi è successo essenzialmente oggi.

 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();