A veces noto programas que se bloquean en mi computadora con el error: "llamada de función virtual pura".
¿Cómo se compilan estos programas cuando no se puede crear un objeto a partir de una clase abstracta?
c++
polymorphism
virtual-functions
pure-virtual
Brian R. Bondy
fuente
fuente
doIt()
llamada en el constructor se desvirtualiza fácilmente y se envía deBase::doIt()
forma estática, lo que solo provoca un error del enlazador. Lo que realmente necesitamos es una situación en la que el tipo dinámico durante un envío dinámico sea el tipo base abstracto.Base::Base
llamar a un no virtualf()
que a su vez llama aldoIt
método virtual (puro) .Además del caso estándar de llamar a una función virtual desde el constructor o destructor de un objeto con funciones virtuales puras, también puede obtener una llamada a 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 algo bastante malo de intentar y hacer, pero si está trabajando con clases abstractas como interfaces y se equivoca, entonces es algo que puede ver. Es posible que sea más probable si está utilizando interfaces contadas referenciadas y tiene un error de recuento de referencias o si tiene una condición de carrera de uso / destrucción de objetos en un programa de subprocesos múltiples ... Lo que pasa con este tipo de purecall es que es A menudo, es menos fácil comprender lo que está sucediendo, ya que una verificación de los "sospechosos habituales" de las llamadas virtuales en ctor y dtor saldrá limpio.
Para ayudar con la depuración de este tipo de problemas, en varias versiones de MSVC, puede reemplazar el controlador purecall de la biblioteca en tiempo de ejecución. Para ello, proporcione su propia función con esta firma:
y vincularlo antes de vincular la biblioteca en tiempo de ejecución. Esto le da a USTED el control de lo que sucede 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 seguimiento de la pila de dónde ocurrió la llamada pura; consulte aquí: http://www.lenholgate.com/blog/2006/01/purecall.html para obtener más detalles.
(Tenga en cuenta que también puede llamar a _set_purecall_handler () para instalar su controlador en algunas versiones de MSVC).
fuente
_purecall()
invocación que normalmente ocurre al llamar a un método de una instancia eliminada no ocurrirá si la clase base ha sido declarada con la__declspec(novtable)
optimización (específica de Microsoft). Con eso, es completamente posible llamar a un método virtual anulado después de que se haya eliminado el objeto, lo que podría enmascarar el problema hasta que lo muerda de alguna otra forma. ¡La_purecall()
trampa es tu amiga!Por lo general, cuando llama 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 razones más "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.
fuente
Me encontré con el escenario en el que se llama a las funciones virtuales puras debido a objetos destruidos,
Len Holgate
ya tengo una respuesta muy agradable , me gustaría agregar algo de color con un ejemplo:El destructor de la clase Derived restablece los puntos vptr a la clase Base vtable, que tiene la función virtual pura, por lo que cuando llamamos a la función virtual, en realidad llama a las virutales puras.
Esto podría suceder debido a un error de código obvio o un escenario complicado de condición de carrera en entornos de subprocesos múltiples.
Aquí hay un ejemplo simple (compilación de g ++ con la optimización desactivada; un programa simple podría optimizarse fácilmente):
Y el seguimiento de la pila se ve así:
Realce:
Si el objeto se elimina por completo, lo que significa que se llama al destructor y se recupera Memroy, es posible que simplemente obtengamos un mensaje
Segmentation fault
porque la memoria ha regresado al sistema operativo y el programa simplemente no puede acceder a él. Por lo tanto, este escenario de "llamada de función virtual pura" generalmente ocurre cuando el objeto se asigna en el grupo de memoria, mientras que se elimina un objeto, la memoria subyacente en realidad no es reclamada por el sistema operativo, todavía está accesible por el proceso.fuente
Supongo que hay un vtbl creado para la clase abstracta por alguna razón interna (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 obtiene. 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 fuera del constructor destructor.
fuente
Yo uso VS2010 y cada vez que intento llamar al destructor directamente desde el método público, obtengo un error de "llamada de función virtual pura" durante el tiempo de ejecución.
Así que moví lo que hay dentro de ~ Foo () para separar el método privado, luego funcionó como un encanto.
fuente
Si usa Borland / CodeGear / Embarcadero / Idera C ++ Builder, puede implementar
Durante la depuración, coloque un punto de interrupción en el código y vea la pila de llamadas en el IDE; de lo contrario, registre la pila de llamadas en su controlador de excepciones (o esa función) si tiene las herramientas adecuadas para ello. Yo personalmente uso MadExcept para eso.
PD. La llamada a la función original está en [C ++ Builder] \ source \ cpprtl \ Source \ misc \ pureerr.cpp
fuente
Aquí hay una forma disimulada de que suceda. Esencialmente me pasó esto hoy.
fuente
I had this essentially happen to me today
obviamente no es cierto, porque simplemente es incorrecto: una función virtual pura se llama solo cuandocallFoo()
se llama dentro de un constructor (o destructor), porque en este momento el objeto está todavía (o ya) en la etapa A. Aquí hay una versión en ejecución de su código sin el error de sintaxisB b();
: los paréntesis la convierten en una declaración de función, desea un objeto.