Anulación de métodos no virtuales

81

Supongamos este escenario en Visual C ++ 2010:

#include <iostream>
#include <conio.h>

using namespace std;

class Base
{
public:
    int b;
    void Display()
    {
        cout<<"Base: Non-virtual display."<<endl;
    };
    virtual void vDisplay()
    {
        cout<<"Base: Virtual display."<<endl;
    };
};

class Derived : public Base
{
public:
    int d;
    void Display()
    {
        cout<<"Derived: Non-virtual display."<<endl;
    };
    virtual void vDisplay()
    {
        cout<<"Derived: Virtual display."<<endl;
    };
};

int main()
{
    Base ba;
    Derived de;

    ba.Display();
    ba.vDisplay();
    de.Display();
    de.vDisplay();

    _getch();
    return 0;
};

En teoría, el resultado de esta pequeña aplicación debería ser:

  • Base: pantalla no virtual.
  • Base: Pantalla virtual.
  • Base: pantalla no virtual.
  • Derivado: Pantalla virtual.

Debido a que el método Display de la clase Base no es un método virtual, la clase Derived no debería poder anularlo. ¿Derecho?

El problema es que cuando ejecuto la aplicación, imprime esto:

  • Base: pantalla no virtual.
  • Base: Pantalla virtual.
  • Derivado: pantalla no virtual.
  • Derivado: Pantalla virtual.

Entonces, o no entendí el concepto de métodos virtuales o algo extraño sucede en Visual C ++.

¿Alguien podría ayudarme con una explicación?

Leif Lazar
fuente
absolutamente tendría Base: pantalla no virtual. al cambiar su línea a de.Base::Display().
v.oddou

Respuestas:

122

Sí, estás malinterpretando un poco.

El método del mismo nombre en la clase derivada ocultará el método principal en este caso. Imaginaría que si este no fuera el caso, intentar crear un método con el mismo nombre que un método no virtual de la clase base arrojaría un error. Está permitido y no es un problema, y ​​si llama al método directamente como lo ha hecho, se llamará bien.

Pero, al no ser virtual, no se utilizarán los mecanismos de búsqueda de métodos de C ++ que permitan el polimorfismo. Entonces, por ejemplo, si creó una instancia de su clase derivada pero llamó a su método 'Display' a través de un puntero a la clase base, se llamará al método base, mientras que para 'vDisplay' se llamará al método derivado.

Por ejemplo, intente agregar estas líneas:

Base *b = &ba;
b->Display();
b->vDisplay();
b = &de;
b->Display();
b->vDisplay();

... y observe el resultado como se esperaba:

Base: pantalla no virtual.
Base: Pantalla virtual.
Base: pantalla no virtual.
Derivado: Pantalla virtual.

sje397
fuente
Hola @ sje397, gracias por tu respuesta. ¿Puedes escribir un ejemplo de cómo llamar al método, como dijiste, mediante un puntero a la clase base? ¡Gracias!
Leif Lazar
También, como dije, TAMBIÉN puede llamar al método base (no virtual) desde la instancia derivada, utilizando la sintaxis de resolución de alcance.
v.oddou
Entonces, solo para estar seguro, puedo definir un método en la clase base y anularlo en la clase derivada, independientemente de declararlo como virtual o no. La única diferencia es que si un puntero base apunta a un objeto de clase derivada, llamar a ese método llamará al método de la clase base si no es virtual y al método de la clase derivada si es virtual. ¿Está bien? ¿Hay alguna otra diferencia?
SexyBeast
@Cupidvogel Sí, eso es correcto. Declararlo 'virtual' significa que C ++ usará mecanismos para admitir polimorfismo y comprobará si hay una versión más derivada del método cuando se llama a través de un puntero de clase base. No puedo pensar en ninguna otra diferencia.
sje397
¿Es suficiente cambiar solo el archivo de encabezado? ¿O debe compilarse la fuente con la palabra clave "virtual"?
Paul Knopf
13

Sí, has entendido mal un poco:

Funciones virtuales puras:

virtual void fun1()=0 -> debe anularse en la clase derivada

Funciones virtuales:

virtual void fun2() -> se puede anular

Funciones normales:

void fun3() -> no lo anule

Para lograr el polimorfismo en tiempo de ejecución, debe anular las funciones virtuales en c ++

Avinash Aitha
fuente
5

Creo que también podría ser mejor mirarlo en el contexto de enlace estático vs dinámico.

Si el método no es virtual (ya está por defecto en C ++ a diferencia de Java), entonces el método se une a su llamador en tiempo de compilación, lo que es imposible conocer el objeto real que se apuntará en tiempo de ejecución. Entonces, el tipo de variable es todo lo que importa, que es la 'Base'.

stdout
fuente