2012-01-11 15 views
30

Duplicar posible:
Can someone explain C++ Virtual Methods?¿Por qué usar funciones virtuales?

Tengo una pregunta con respecto a las funciones virtuales de C++.

¿Por qué y cuándo usamos las funciones virtuales? ¿Alguien puede darme una implementación en tiempo real o el uso de funciones virtuales?

+1

Creé una publicación de blog solo por este motivo, porque me resultó difícil explicarlo cuando un niño me preguntó "¿por qué funciones virtuales? http://nrecursions.blogspot.in/2015/06/so-why-do-we-need-virtual-functions.html – Nav

Respuesta

45

Utiliza funciones virtuales cuando desea anular un cierto comportamiento (método de lectura) para su clase derivada en lugar del implementado para la clase base y desea hacerlo en tiempo de ejecución mediante un puntero a la clase base .

El ejemplo clásico es cuando tienes una clase base llamada Shape y formas concretas (clases) que se derivan de ella. Cada clase concreta sobrescribe (implementa un método virtual) llamada Draw().

la jerarquía de clases es el siguiente:

Class hierarchy

El siguiente fragmento muestra el uso del ejemplo; crea una matriz de punteros de clase Shape en donde cada uno apunta a un objeto de clase derivado distinto. En tiempo de ejecución, la invocación del método Draw() da como resultado la invocación del método anulado por esa clase derivada y se dibuja (o representa) el Shape particular.

Shape *basep[] = { &line_obj, &tri_obj, 
        &rect_obj, &cir_obj}; 
for (i = 0; i < NO_PICTURES; i++) 
    basep[i] -> Draw(); 

El programa anterior simplemente utiliza el puntero a la clase base para almacenar las direcciones de los objetos de clase derivados. Esto proporciona un acoplamiento flexible porque el programa no tiene que cambiar drásticamente si se agrega una nueva clase derivada de concreto de shape en cualquier momento. La razón es que hay segmentos de código mínimos que realmente usan (dependen) en el tipo concreto Shape.

Lo anterior es un buen ejemplo del Open Closed Principle de los famosos principios de diseño SOLID.

+7

¿Qué pasa si no usamos 'virtual' y simplemente redefinimos en subclases como' Line 'y' Triángulo'. ¿Cuál sería la diferencia allí? – bluejamesbond

+4

Si la función no era virtual en la clase base, no se podía acceder a la versión de clases derivadas a través del puntero de la clase base. – radensb

+0

Lo siento, todavía estoy un poco confundido aquí. ¿Sería correcto decir que las funciones virtuales son para cuando usted no sabe qué versión de una función necesitará hasta el tiempo de ejecución, pero también necesitará una instanciación de la clase base algún tiempo antes de eso? Porque parece que todavía podría pasar sin funciones virtuales y sin saber lo que necesito hasta el tiempo de ejecución simplemente creando subclases y creando una instancia de la clase adecuada una vez que lo sepa. Entonces parece que * también necesitarás una instanciación de la clase base algún tiempo antes * es crítico, ¿verdad? Aka: llamando a la función derivada a través del puntero base – krb686

1

Utilizaría una función virtual para implementar "polimorfismo", en particular cuando tiene un objeto, no sabe cuál es el tipo subyacente real, pero sabe qué operación desea realizar en él, y la implementación de esto (cómo lo hace) difiere según el tipo que realmente tenga.

Esencialmente lo que comúnmente se llama el "principio de sustitución de liskov" el nombre de Barbara Liskov que habló de esto alrededor de 1983.

Cuando es necesario utilizar las decisiones de ejecución dinámicos en los que, en el punto se llama el código que invoca la función , usted no sabe qué tipos pueden atravesarlo, ya sea ahora o en el futuro, este es un buen modelo para usar.

No es la única manera sin embargo. Hay todo tipo de "devoluciones de llamada" que pueden generar una "acumulación" de datos y es posible que tenga tablas de devolución de llamadas que dependen de un bloque de encabezado en los datos que entran, p. Ej. un procesador de mensajes. Para esto no es necesario usar una función virtual, de hecho, lo que probablemente usaría es una especie de cómo una tabla v se implementa solo con una entrada (por ejemplo, una clase con una sola función virtual).

20

Piense en la clase de animales, y se deriva de ella son el gato, el perro y la vaca.La clase animal tiene un

virtual void SaySomething() 
{ 
    cout << "Something"; 
} 

función.

Animal *a; 
a = new Dog(); 
a->SaySomething(); 

En lugar de imprimir "Something", el perro debería decir "Bark", el gato debería decir "Meow". En este ejemplo, ves que a es un perro, pero algunas veces tienes un puntero de animal y no sabes de qué animal se trata. No quiere saber qué animal es, solo quiere que el animal diga algo. Así que solo llama a la función virtual y los gatos dicen "miau" y los perros dicen "ladran".

Por supuesto, la función SaySomething debería haber sido puramente virtual para evitar posibles errores.

+2

Gracias, su respuesta fue satisfactoria .... – haris

+0

No estoy del todo acerca de su última oración "La función SaySomething debería haber sido puramente virtual para evitar posibles errores". podría por favor hacer un ejemplo de esto. – user454083

+3

Perdón por la respuesta tardía. Considere esto, un desarrollador crea una nueva clase que hereda nuestra clase de animales (por ejemplo, 'Fox') y se olvida de anular el método SaySomething.Si usa el método virtual que proporcioné, las instancias 'Fox' dirá" Something ", lo cual no es correcto. Si declaramos SaySomething como puramente virtual, no podríamos instanciar 'Fox', un código que contenga' nuevo Fox (...) 'generará un error. De esta forma, el desarrollador que creó la clase 'Fox' recibirá una notificación sobre su error en tiempo de compilación. Los errores de tiempo de compilación son buenos, ya que no pierden tiempo :) – holgac

20

Utiliza funciones virtuales cuando necesita manejar diferentes objetos de la misma manera. Se llama polimorfismo. Vamos a imaginar que tiene alguna clase de base - algo así como la forma clásica:

class Shape 
    { 
     public: 
      virtual void draw() = 0; 
      virtual ~Shape() {} 
    }; 

    class Rectange: public Shape 
    { 
     public: 
      void draw() { // draw rectangle here } 
    }; 


    class Circle: public Shape 
    { 
     public: 
      void draw() { // draw circle here } 
    }; 

Ahora usted puede tener vectores de diferentes formas:

vector<Shape*> shapes; 
    shapes.push_back(new Rectangle()); 
    shapes.push_back(new Circle()); 

y se puede extraer todas las formas de esta manera:

for(vector<Shape*>::iterator i = shapes.begin(); i != shapes.end(); i++) 
    { 
      (*i)->draw(); 
    } 

De esta manera está dibujando diferentes formas con un método virtual - draw(). La versión correcta del método se selecciona según la información del tiempo de ejecución sobre el tipo de objeto detrás del puntero.

Aviso Al utilizar las funciones virtuales se pueden declarar como virtual pura (como en la forma de clases, solo lugar "= 0" después proto método). En este caso, no podrá crear una instancia de objeto con función virtual pura y se llamará clase abstracta.

También observe "virtual" antes del destructor. En el caso de que esté planeando trabajar con objetos mediante punteros a sus clases base, debe declarar el destructor virtual, de modo que cuando llame a "eliminar" para el puntero de clase base, se invocará toda la cadena de destructores y no habrá pérdidas de memoria.

Cuestiones relacionadas