2010-01-02 18 views
6

Me parece extraño. En el ctor de Sample_Base, invoco bar() que internamente llama a fun(), que es una función virtual pura. Me sale el error llamado "función virtual pura". Lo cual está bien. Ahora, si invoco fun() directamente desde el ctor de Sample_Base, no obtengo ese error. Lo probé en VC++ 2010 Beta 2 y en g ++ 4.4.1 en Ubuntu 9.10. Estoy de acuerdo en que dar una implementación para la función virtual pura, que no sea el destructor virtual puro, no tiene sentido. Pero, estoy un poco sorprendido acerca de este comportamiento.Función virtual pura llamada error

class Sample_Base 
{ 
public: 
    Sample_Base() 
    { 
     bar(); 
     // fun(); 
    } 
    /* This is code does not throw any error. 
    Sample_Base() 
    { 
     fun(); 
    } 
    */ 

    void bar() 
    { 
     fun(); 
    } 
    virtual void fun() = 0; 
    virtual ~Sample_Base(); 
}; 

Sample_Base::~Sample_Base() 
{ 

} 

void Sample_Base::fun() 
{ 
    std::cout << "Sample_Base::fun\n"; 
} 

class Sample_Derived : public Sample_Base 
{ 
public: 
    Sample_Derived() : Sample_Base() 
    { 
     fun(); 
    } 

    void fun() 
    { 
     std::cout << "Sample_Derived::fun\n"; 
    } 

    ~Sample_Derived() 
    { 

    } 
}; 

Respuesta

6

Cuando llama a la función directamente, ya que está en el constructor, el compilador resuelve el tipo estático de su objeto (Sample_Base) y llama directamente al Sample_Base::fun(). Dado que proporcionó una implementación para él, el compilador encuentra la función y funciona.

Cuando lo llamas indirectamente, a través de bar(), el compilador debe usar el tipo dinámico, por lo que realiza una llamada virtual que se resuelve en el tiempo de ejecución. Y allí falla, porque llama a una función virtual pura.

Entonces la diferencia está en el momento en que vincula la función a la llamada.

+0

El [Estándar de C++] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf) §10.4.6 establece que "una llamada virtual a una función virtual pura" ... para el objeto que se está creando ... no está definido ". ¿Puedes comentar sobre eso? Lo leería diciendo que llamar 'fun' directamente también debería estar indefinido (incluso si el compilador no produce un programa bloqueado en este caso específico). O no es una llamada virtual en ese contexto (si es así, por qué, no pude encontrarlo en el estándar). – Xlea

+0

@ Xlea Charles Bailey lo dirige a las secciones correctas del estándar en su respuesta. Eso sí, estaba usando el estándar C++ 03 en ese entonces, por lo que los números de las secciones serán diferentes ahora, pero eso puede guiarte. – Gorpik

1

Llamar a una función virtual no invocará las funciones superiores en las clases derivadas. Llamar a una función virtual pura en un constructor o destructor es Comportamiento indefinido.

que podría estar interesado en la lectura y thisthis.

1

En el momento de la construcción, cuando el constructor Sample_Base se llama, el objeto no está construido completamente todavía. Específicamente, las partes que pertenecen a Sample_Derived aún no son llamadas creadas a funciones virtuales que se sobrescribirán por Sample_Derived no llamarán a la implementación en Sample_Derived, pero la versión se definió en Sample_Base. Y como la función no tiene implementación, se obtiene un error.

Para obtener más información y posibles soluciones también vea this entry in the C++ FAQ Lite.

+0

Acepto que el objeto no está completamente construido cuando se llama a la barra en Sample_Base, pero cuando invoco fun() desde el constructor de Sample_Base, no obtengo ningún error. Eso es lo que me interesa. – Jagannath

+0

@Jagannath: Recibir o no recibir el error no es lo que debe preocuparse, debe preocuparse por el hecho de que este diseño es peligroso. Debería intentar evitar llamar a métodos virtuales desde su constructor/destructor. –

+0

@Gal: Sé que el diseño no es correcto y se debe evitar llamar a la función virtual desde un constructor. Solo estoy interesado en la diferencia en el comportamiento. Gracias de todos modos. – Jagannath

4

Proporcionar una definición para una función virtual pura no es necesariamente sin sentido. Marcar una función virtual pura significa que la clase adjunta es abstracta y que cualquier clase que se derive de ella es abstracta a menos que la anulación final para esa función no sea una función virtual pura. Aún se puede llamar a una función virtual pura mediante una llamada explícita no virtual.

En el cuerpo de un constructor de la clase base (pero no de un ctor-inicializador) la versión de una función virtual llamada a través de una llamada virtual es uno definido en la clase en sí o una de sus bases y no de cualquier clase anulándolo (que aún no se habría construido). Esto se especifica explícitamente en 12.7 [class.cdtor]/3.

Es legal llamar explícitamente a una función virtual pura en un cuerpo constructor (es decir, utilizando un calificador explícito de clase) -aunque esto requeriría que la función tenga un cuerpo definido- pero es un comportamiento indefinido llamar a una función virtual pura a través de una llamada virtual que solo es posible desde el constructor o el destructor de una clase abstracta. Esto se especifica explícitamente en 10.4 [class.abstract]/6.

1

Este comportamiento no está indefinido, se define explícitamente: las funciones virtuales no son virtuales en constructores y destructores. Llaman a la versión estática de la función. Si la función es puramente virtual, esto produce el famoso error de "llamada virtual pura" en VC.

He visto una variación divertida de esto en un programa multiproceso: un objeto se está destruyendo en el hilo A, mientras que el hilo B está intentando llamar a una función virtual. No hubo una llamada de función virtual en el constructor o el destructor, pero todavía recibimos un error de llamada virtual pura.

Cuestiones relacionadas