2009-09-10 9 views
20

Supongo que si foo se declara en la clase D, pero no se marca virtual, entonces el siguiente código llamaría a la implementación de foo en D (independientemente del tipo dinámico de d).En C++, ¿es una función automáticamente virtual si anula una función virtual?

D& d = ...; 
d.foo(); 

Sin embargo, en el siguiente programa, ese no es el caso. ¿Alguien puede explicar esto? ¿Es un método automáticamente virtual si anula una función virtual?

#include <iostream> 

using namespace std; 

class C { 
public: 
     virtual void foo() { cout << "C" << endl; } 
}; 

class D : public C { 
public: 
     void foo() { cout << "D" << endl; } 
}; 

class E : public D { 
public: 
     void foo() { cout << "E" << endl; } 
}; 

int main(int argc, char **argv) 
{ 
     E& e = *new E; 
     D& d = *static_cast<D*>(&e); 
     d.foo(); 
     return 0; 
} 

La salida del programa anterior es:

E 
+4

La static_cast es superfluo - 'D & d = * static_cast (&e);' es equivalente a 'D & d = e;' debido a la conversión implícita de E */E & a D */D & . –

+0

En C++ 11 al agregar "anular" a la declaración de función deja en claro su intención de anular la función de clase base. También desencadena un error del compilador en caso de que la función que declara difiera en constness de base (algo que pueda sorprenderte en caso de que derives de std :: exception por ejemplo y declares qué() no const) – Ghita

Respuesta

22

estándar 10.3.2 (class.virtual) dice:

Si un vf función miembro virtual se declaró en una clase base y en una clase derivada, que se deriven directa o indirectamente de la base, una función miembro vf con el mismo nombre y la misma lista de parámetros que Base :: vf se declara, Derived :: vf también es virtual (esté o no declarado) y reemplaza *

[Nota: una función con el mismo nombre pero una lista de parámetros diferente (cláusula over) como función virtual no es necesariamente virtual y no anula. El uso del especificador virtual en la declaración de una función de anulación es legal pero redundante (tiene semántica vacía). El control de acceso (cláusula class.access) no se considera al determinar la anulación. --- extremo foonote]

17

respuesta rápida puede ser que no, pero la respuesta correcta es

C++ no sabe nada de ocultación función, así anulando función virtual sin marcas de palabras clave virtuales que también funcionan virtuales.

+11

@Yossarian, es por eso que se considera buena práctica declarar las funciones virtuales en clases derivadas como virtuales para que la intención sea clara. Scott Meyers discute esto en su libro Effective C++. –

+0

He oído en alguna parte que no marcar virtual en los métodos derivados los haría virtualmente definitivos. lo que hace que la derivada de tercer nivel sea una anulación no virtual. pero encuentro que el extracto del estándar de Tadeusz está demostrando que esta creencia está equivocada debido a la palabra "indirectamente", ¿no es así? –

+2

@ v.oddou "He oído en alguna parte" que las cosas que las personas "oyeron en alguna parte" son generalmente falsas. ;-) Mientras haya una función con la misma firma marcada como "virtual" _en alguna parte_ más arriba en la jerarquía, entonces cualquier función derivada con la misma firma es una anulación. "Brechas" en si la declaración incluida 'virtual' no es relevante, ya que de nuevo, la palabra clave tiene semántica vacía después de su apariencia más básica. –

0

No está creando ninguna copia del objeto de ey lo está colocando en d. Entonces d.foo() sigue el comportamiento polimórfico normal y llama al método de clase derivado. Un método que se declara como virtual en la clase base también se convierte automáticamente en virtual en la clase derivada.

-1

La salida ("E") se comporta exactamente como se esperaría que se comportara.

El motivo: El tipo dinámico (es decir, el tiempo de ejecución) de esa referencia es E. Usted está realizando una actualización estática a D, pero eso no cambia el tipo real del objeto, por supuesto.

Esa es la verdadera idea detrás de los métodos virtuales y los despachos dinámicos: Verá el comportamiento del tipo que estaba instanciando, que es E, en este caso.

Cuestiones relacionadas