Sobreescritura vs Virtual


¿Cuál es el propósito de usar la palabra reservada virtual frente a funciones? Si quiero que una clase hija anule una función padre, simplemente declaro la misma función como void draw(){}.

class Parent { 
public:
    void say() {
        std::cout << "1";
    }
};

class Child : public Parent {
public:
    void say()
    {
        std::cout << "2";
    }
};

int main()
{
    Child* a = new Child();
    a->say();
    return 0;
}

La salida es 2.

De nuevo, ¿por qué la palabra reservada virtual sería necesaria en el encabezado de say()?

Muchas gracias.

Author: nonsensickle, 2010-05-29

7 answers

Esta es la pregunta clásica de cómo funciona el polimorfismo, creo. La idea principal es que desea abstraer el tipo específico para cada objeto. En otras palabras: Usted quiere ser capaz de llamar a las instancias Hijo sin saber que es un niño!

Aquí hay un ejemplo: Asumiendo que usted tiene clase "Hijo" y clase "Hijo 2" y "Hijo 3" usted quiere ser capaz de referirse a ellos a través de su clase base (Padre).

Parent[3] parents;
parents[0] = new Child();
parents[1] = new Child2();
parents[2] = new Child3();

for (int i=0;i<3;++i)
 parents[i]->say();

Como pueden imaginar, esto es muy poderoso. Te permite extender la Padre tantas veces como desee y las funciones que toman un puntero padre seguirán funcionando. Para que esto funcione como otros mencionan, debe declarar el método como virtual.

 16
Author: krolth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-01 06:26:01

Si la función fuera virtual, entonces podría hacer esto y aún así obtener la salida"2":

Parent* a = new Child();
a->say();

Esto funciona porque una función virtual usa el tipo actual mientras que una función no virtual usa el tipo declarado. Lea sobrepolimorfismo para una mejor discusión de por qué querría hacer esto.

 36
Author: Donnie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-28 22:32:37

Pruébalo con:

Parent *a = new Child();
Parent *b = new Parent();

a->say();
b->say();

Sin virtual, ambos con la letra "1". Agregue virtual, y el niño actuará como un Niño, a pesar de que se le hace referencia a través de un puntero a Parent.

 24
Author: Jerry Coffin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-28 22:33:44

Si no utiliza la palabra clave virtual no está sobreescribiendo, sino que rahter define un método no relacionado en la clase derivada que ocultará el método de la clase base. Es decir, sin la virtual, Base::say y Derived::say no están relacionados besides además de la coincidencia de nombre.

Cuando utiliza la palabra clave virtual (requerida en la base, opcional en la clase derivada), le está diciendo al compilador que las clases que derivan de esta base podrán anular el método. En ese caso, Base::say y Derived::say se consideran sobreescrituras del mismo método.

Cuando se usa una referencia o puntero a una clase base para llamar a un método virtual, el compilador agregará el código apropiado para que se llame al overrider final (el override en la clase más derivada que define el método en la jerarquía de la instancia concreta en uso). Tenga en cuenta que si no usa referencias / puntero sino variables locales, el compilador puede resolver la llamada y no necesita usar el despacho virtual mecanismo.

 15
Author: David Rodríguez - dribeas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-28 22:43:32

Bueno, lo probé por mí mismo, porque hay muchas cosas en las que podemos pensar:

#include <iostream>
using namespace std;
class A
{
public:
    virtual void v() { cout << "A virtual" << endl; }
    void f() { cout << "A plain" << endl; }
};

class B : public A
{
public:
    virtual void v() { cout << "B virtual" << endl; }
    void f() { cout << "B plain" << endl; }
};

class C : public B
{
public:
    virtual void v() { cout << "C virtual" << endl; }
    void f() { cout << "C plain" << endl; }
};

int main()
{
    A * a = new C;
    a->f();
    a->v();

    ((B*)a)->f();
    ((B*)a)->v();
}

Salida:

A plain
C virtual
B plain
C virtual

Creo que una respuesta buena, simple y corta podría verse así (porque creo que las personas que pueden entender más pueden memorizar menos, por lo tanto, necesitan una explicación corta y simple):

Los métodos virtuales comprueban los DATOS de la instancia a la que apunta el puntero, mientras que los métodos clásicos no llaman al método que corresponde al especificado tipo.

El punto de esa característica es el siguiente: supongamos que tiene una matriz de A. La matriz puede contener B, C, (o incluso tipos derivados. ). si desea llamar secuencialmente al mismo método de todas esas instancias, llamaría a cada una que sobrecargó.

Encuentro esto bastante difícil de entender, y obviamente cualquier curso de C++ debería explicar cómo se logra esto, porque la mayoría de las veces solo se te enseña sobre funciones virtuales, las usas, pero hasta que entiendes cómo el compilador los entiende y cómo el ejecutable manejará las llamadas, usted está en la oscuridad.

Lo que pasa con VFtables es que nunca me han explicado qué tipo de código agrega, y eso es obviamente aquí donde C++ requiere mucha más experiencia que C, y esta podría ser la razón principal por la que C++ fue etiquetado como "lento" en sus primeros días: de hecho, es poderoso, pero como todo, es poderoso si sabes cómo usarlo, o simplemente "te vuelas toda la pierna".

 12
Author: jokoon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-20 15:00:19

Cuando se utiliza la palabra clave virtual, se crea una tabla de funciones virtuales para localizar los métodos correctos en una instancia. Entonces, incluso si la instancia derivada es apuntada por un puntero de clase base, todavía encontrará la implementación correcta del método.

 2
Author: Amardeep AC9MF,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-28 22:32:03

Este es un aspecto muy importante de la programación en c++ almost casi todas las entrevistas a las que he ido, me hacen esta pregunta.

¿Qué sucede si cambia su main a:

int main() { Parent* a = new Child(); a->say(); return 0; }

También, vale la pena entender lo que es un vtable.

 -1
Author: user353306,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-07 01:35:41