¿De dónde vienen los lockings de “llamadas de funciones virtuales puras”?

A veces veo progtwigs que se cuelgan en mi computadora con el error: “llamada de función virtual pura”.

¿Cómo comstackn estos progtwigs incluso cuando no se puede crear un objeto de una clase abstracta?

Pueden resultar si intenta realizar una llamada de función virtual desde un constructor o destructor. Como no puede realizar una llamada de función virtual desde un constructor o destructor (el objeto de clase derivado no se ha construido o ya se ha destruido), llama a la versión de clase base, que en el caso de una función virtual pura, doesn existo

(Vea la demostración en vivo aquí )

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 } 

Además del caso estándar de invocar una función virtual desde el constructor o destructor de un objeto con funciones virtuales puras, también puede obtener una llamada de función virtual pura (al menos en MSVC) si llama a una función virtual después de que el objeto ha sido destruido. . Obviamente, esto es bastante malo intentarlo, pero si trabajas con clases abstractas como interfaces y te equivocas, entonces es algo que podrías ver. Posiblemente sea más probable si está usando interfaces contadas de referencia y tiene un error de recuento de ref o si tiene una condición de carrera de uso de objeto / destrucción de objeto en un progtwig de subprocesos múltiples … Lo que pasa con estos tipos de llamadas puros es que es a menudo es menos fácil comprender lo que está sucediendo, ya que un cheque para los “sospechosos habituales” de llamadas virtuales en Ctor y Dtor saldrá limpio.

Para ayudar con la depuración de este tipo de problemas, puede, en diversas versiones de MSVC, reemplazar el controlador purecall de la biblioteca de tiempo de ejecución. Usted hace esto al proporcionar su propia función con esta firma:

 int __cdecl _purecall(void) 

y vincularlo antes de vincular la biblioteca de tiempo de ejecución. Esto le da a USTED control de lo que ocurre cuando se detecta una llamada pura. Una vez que tenga el control, puede hacer algo más útil que el controlador estándar. Tengo un controlador que puede proporcionar un rastro de stack de donde ocurrió el llamado puro; ver aquí: http://www.lenholgate.com/blog/2006/01/purecall.html para más detalles.

(Tenga en cuenta que también puede llamar a _set_purecall_handler () para instalar su controlador en algunas versiones de MSVC).

Por lo general, cuando llamas a una función virtual a través de un puntero colgante, lo más probable es que la instancia ya haya sido destruida.

También puede haber más razones “creativas”: tal vez haya logrado cortar la parte de su objeto donde se implementó la función virtual. Pero generalmente es solo que la instancia ya ha sido destruida.

Supongo que hay un vtbl creado para la clase abstracta por algún motivo interno (podría ser necesario para algún tipo de información de tipo de tiempo de ejecución) y algo sale mal y un objeto real lo recibe. Es un error. Solo eso debería decir que algo que no puede suceder es.

Pura especulación

editar: parece que estoy equivocado en el caso en cuestión. OTOH IIRC algunos lenguajes permiten llamadas vtbl desde el constructor destructor.

Yo uso VS2010 y cada vez que bash llamar al destructor directamente desde el método público, aparece un error de “llamada de función virtual pura” durante el tiempo de ejecución.

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

Así que moví lo que había adentro ~ Foo () para separar el método privado, luego funcionó a las mil maravillas.

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

Si utiliza Borland / CodeGear / Embarcadero / Idera C ++ Builder, puede implementarlo

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

Durante la depuración, coloque un punto de interrupción en el código y vea la stack de llamadas en el IDE; de lo contrario, registre la stack de llamadas en su controlador de excepción (o en esa función) si tiene las herramientas adecuadas para eso. Yo personalmente uso MadExcept para eso.

PD. La llamada a la función original está en [C ++ Builder] \ source \ cpprtl \ Source \ misc \ pureerr.cpp

Aquí hay una manera furtiva de que suceda. Esto me pasó esencialmente hoy.

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