C ++ Virtual / Pure Virtual Explained

¿Qué significa exactamente si una función se define como virtual y es lo mismo que puramente virtual?

De la función virtual de Wikipedia …

Una función virtual o método virtual es una función o método cuyo comportamiento puede ser anulado en una clase heredada por una función con la misma firma

mientras..

Una función virtual pura o método virtual puro es una función virtual que se requiere para ser implementada por una clase derivada que no sea abstracta “- Wikipedia

Entonces, la función virtual puede ser anulada y la virtual pura debe ser implementada.

Me gustaría comentar sobre la definición de virtual de Wikipedia, como lo repiten varios aquí. [En el momento en que se escribió esta respuesta,] Wikipedia definió un método virtual como uno que se puede anular en subclases. [Afortunadamente, Wikipedia se ha editado desde entonces, y ahora lo explica correctamente.] Eso es incorrecto: cualquier método, no solo los virtuales, se puede anular en subclases. Lo que hace Virtual es darle polymorphism, es decir, la capacidad de seleccionar en tiempo de ejecución la anulación más derivada de un método .

Considera el siguiente código:

 #include  using namespace std; class Base { public: void NonVirtual() { cout < < "Base NonVirtual called.\n"; } virtual void Virtual() { cout << "Base Virtual called.\n"; } }; class Derived : public Base { public: void NonVirtual() { cout << "Derived NonVirtual called.\n"; } void Virtual() { cout << "Derived Virtual called.\n"; } }; int main() { Base* bBase = new Base(); Base* bDerived = new Derived(); bBase->NonVirtual(); bBase->Virtual(); bDerived->NonVirtual(); bDerived->Virtual(); } 

¿Cuál es la salida de este progtwig?

 Base NonVirtual called. Base Virtual called. Base NonVirtual called. Derived Virtual called. 

Derivado anula todos los métodos de Base: no solo el virtual, sino también el no virtual.

Vemos que cuando tienes un Base-pointer-to-Derived (bDerived), llamar a NonVirtual llama a la implementación de la clase Base. Esto se resuelve en tiempo de comstackción: el comstackdor ve que bDerived es una Base *, que NonVirtual no es virtual, por lo que hace la resolución en la clase Base.

Sin embargo, llamar a Virtual llama a la implementación de la clase Derivada. Debido a la palabra clave virtual, la selección del método ocurre en tiempo de ejecución , no en tiempo de comstackción. Lo que sucede aquí en tiempo de comstackción es que el comstackdor ve que se trata de una Base * y que está llamando a un método virtual, por lo que inserta una llamada al vtable en lugar de la clase Base. Este vtable se crea una instancia en el tiempo de ejecución, de ahí la resolución en tiempo de ejecución a la anulación derivada más.

Espero que esto no sea demasiado confuso. En resumen, cualquier método puede ser anulado, pero solo los métodos virtuales le dan polymorphism, es decir, selección en tiempo de ejecución de la anulación más derivada. En la práctica, sin embargo, anular un método no virtual se considera una mala práctica y rara vez se utiliza, por lo que muchas personas (incluido el que escribió ese artículo de Wikipedia) piensan que solo se pueden anular los métodos virtuales.

La palabra clave virtual le da a C ++ su capacidad para soportar el polymorphism. Cuando tiene un puntero a un objeto de alguna clase, como:

 class Animal { public: virtual int GetNumberOfLegs() = 0; }; class Duck : public Animal { public: int GetNumberOfLegs() { return 2; } }; class Horse : public Animal { public: int GetNumberOfLegs() { return 4; } }; void SomeFunction(Animal * pAnimal) { cout < < pAnimal->GetNumberOfLegs(); } 

En este ejemplo (absurdo), la función GetNumberOfLegs () devuelve el número apropiado en función de la clase del objeto para el que se solicita.

Ahora, considere la función ‘SomeFunction’. No importa qué tipo de objeto animal se le pase, siempre que se derive de Animal. El comstackdor automáticamente lanzará cualquier clase derivada de Animal a un Animal ya que es una clase base.

Si hacemos esto:

 Duck d; SomeFunction(&d); 

saldría ‘2’. Si hacemos esto:

 Horse h; SomeFunction(&h); 

saldría ‘4’. No podemos hacer esto:

 Animal a; SomeFunction(&a); 

porque no se comstackrá debido a que la función virtual GetNumberOfLegs () es pura, lo que significa que debe implementarse derivando clases (subclases).

Las funciones virtuales puras se utilizan principalmente para definir:

a) clases abstractas

Estas son clases base donde debe derivar de ellas y luego implementar las funciones virtuales puras.

b) interfaces

Estas son clases “vacías” en las que todas las funciones son puramente virtuales y, por lo tanto, debe derivar y luego implementar todas las funciones.

En una clase de C ++, virtual es la palabra clave que designa eso, un método puede ser anulado (es decir, implementado por) una subclase. Por ejemplo:

 class Shape { public: Shape(); virtual ~Shape(); std::string getName() // not overridable { return m_name; } void setName( const std::string& name ) // not overridable { m_name = name; } protected: virtual void initShape() // overridable { setName("Generic Shape"); } private: std::string m_name; }; 

En este caso, una subclase puede anular la función initShape para realizar un trabajo especializado:

 class Square : public Shape { public: Square(); virtual ~Square(); protected: virtual void initShape() // override the Shape::initShape function { setName("Square"); } } 

El término virtual puro se refiere a las funciones virtuales que necesitan ser implementadas por una subclase y no han sido implementadas por la clase base. Usted designa un método como virtual puro usando la palabra clave virtual y agregando a = 0 al final de la statement del método.

Entonces, si quisieras hacer Shape :: initShape pure virtual, harías lo siguiente:

 class Shape { ... virtual void initShape() = 0; // pure virtual method ... }; 

Al agregar un método virtual puro a su clase, la clase se convierte en una clase base abstracta que es muy útil para separar las interfaces de la implementación.

“Virtual” significa que el método puede ser anulado en subclases, pero tiene una implementación directa en la clase base. “Pure virtual” significa que es un método virtual sin implementación directa. Tal método debe ser anulado al menos una vez en la jerarquía de herencia: si una clase tiene algún método virtual no implementado, los objetos de esa clase no se pueden construir y la comstackción fallará.

@quark señala que los métodos puros virtuales pueden tener una implementación, pero como los métodos puramente virtuales deben ser anulados, la implementación predeterminada no puede ser llamada directamente. Aquí hay un ejemplo de un método de pureza virtual con un valor predeterminado:

 #include  class A { public: virtual void Hello() = 0; }; void A::Hello() { printf("A::Hello\n"); } class B : public A { public: void Hello() { printf("B::Hello\n"); A::Hello(); } }; int main() { /* Prints: B::Hello A::Hello */ B b; b.Hello(); return 0; } 

Según los comentarios, si la comstackción fallará o no es específica del comstackdor. En GCC 4.3.3 al menos, no comstackrá:

 class A { public: virtual void Hello() = 0; }; int main() { A a; return 0; } 

Salida:

 $ g++ -c virt.cpp virt.cpp: In function 'int main()': virt.cpp:8: error: cannot declare variable 'a' to be of abstract type 'A' virt.cpp:1: note: because the following virtual functions are pure within 'A': virt.cpp:3: note: virtual void A::Hello() 

¿Cómo funciona la palabra clave virtual?

Supongamos que el hombre es una clase base, el indio se deriva del hombre.

 Class Man { public: virtual void do_work() {} } Class Indian : public Man { public: void do_work() {} } 

Declarar do_work () como virtual simplemente significa: que do_work () para llamar se determinará ÚNICAMENTE en tiempo de ejecución.

Supongamos que lo hago,

 Man *man; man = new Indian(); man->do_work(); // Indian's do work is only called. 

Si no se usa virtual, el comstackdor lo determina estáticamente o estático, según el objeto al que llama. Entonces, si un objeto de Man llama a do_work (), el do_work () del hombre se llama AUN AUNQUE PUNTE A UN OBJETO INDIO

Creo que la respuesta más votado es engañosa: cualquier método sea virtual o no puede tener una implementación anulada en la clase derivada. Con referencia específica a C ++, la diferencia correcta es el tiempo de ejecución (cuando se usa virtual) y el tiempo de comstackción (cuando no se usa virtual pero se anula un método y se apunta un puntero base a un objeto derivado) de las funciones asociadas.

Parece que hay otro comentario engañoso que dice:

“Justin, ‘virtual puro’ es solo un término (no una palabra clave, vea mi respuesta a continuación) que solía significar que” esta función no puede ser implementada por la clase base “.

¡ESTO ESTÁ MAL! ¡Las funciones puramente virtuales también pueden tener un cuerpo Y PUEDEN SER IMPLEMENTADAS! ¡La verdad es que la función virtual pura de una clase abstracta se puede llamar estáticamente! Dos muy buenos autores son Bjarne Stroustrup y Stan Lippman … porque escribieron el lenguaje.

Simula, C ++ y C #, que usan enlace de método estático de forma predeterminada, el progtwigdor puede especificar que los métodos particulares deben usar enlace dynamic etiquetándolos como virtuales. La vinculación dinámica de métodos es fundamental para la progtwigción orientada a objetos.

La progtwigción orientada a objetos requiere tres conceptos fundamentales: encapsulación, herencia y enlace de método dynamic.

La encapsulación permite que los detalles de implementación de una abstracción se oculten detrás de una interfaz simple.

La herencia permite que una nueva abstracción se defina como una extensión o refinamiento de alguna abstracción existente, obteniendo algunas o todas sus características automáticamente.

La vinculación dinámica de métodos permite que la nueva abstracción muestre su nuevo comportamiento incluso cuando se usa en un contexto que espera la abstracción anterior.

Los métodos virtuales PUEDEN ser anulados derivando clases, pero necesitan una implementación en la clase base (la que será anulada)

Los métodos virtuales puros no tienen implementación en la clase base. Deben definirse por clases derivadas. (Así que técnicamente anulado no es el término correcto, porque no hay nada que anular).

Virtual corresponde al comportamiento java predeterminado, cuando la clase derivada anula un método de la clase base.

Los métodos virtuales puros corresponden al comportamiento de los métodos abstractos dentro de las clases abstractas. Y una clase que solo contenga métodos y constantes virtuales puros sería el cpp-colgante de una interfaz.

Función virtual pura

prueba este código

 #include  using namespace std; class aClassWithPureVirtualFunction { public: virtual void sayHellow()=0; }; class anotherClass:aClassWithPureVirtualFunction { public: void sayHellow() { cout< <"hellow World"; } }; int main() { //aClassWithPureVirtualFunction virtualObject; /* This not possible to create object of a class that contain pure virtual function */ anotherClass object; object.sayHellow(); } 

En la clase anotherClass elimina la función sayHellow y ejecuta el código. obtendrá un error! Porque cuando una clase contiene una función virtual pura, no se puede crear ningún objeto a partir de esa clase y se hereda, entonces su clase derivada debe implementar esa función.

Función virtual

prueba otro código

 #include  using namespace std; class aClassWithPureVirtualFunction { public: virtual void sayHellow() { cout< <"from base\n"; } }; class anotherClass:public aClassWithPureVirtualFunction { public: void sayHellow() { cout<<"from derived \n"; } }; int main() { aClassWithPureVirtualFunction *baseObject=new aClassWithPureVirtualFunction; baseObject->sayHellow();///call base one baseObject=new anotherClass; baseObject->sayHellow();////call the derived one! } 

Aquí la función sayHellow se marca como virtual en la clase base. Dice el comstackdor que intenta buscar la función en la clase derivada e implementa la función. Si no se encuentra, ejecuta la base. Gracias.

“Una función virtual o método virtual es una función o método cuyo comportamiento puede ser anulado dentro de una clase heredada por una función con la misma firma” – wikipedia

Esta no es una buena explicación para las funciones virtuales. Porque, incluso si un miembro no es virtual, heredar clases puede anularlo. Puedes intentarlo y verlo tú mismo.

La diferencia se muestra cuando una función toma una clase base como parámetro. Cuando se da una clase heredada como entrada, esa función usa la implementación de la clase base de la función anulada. Sin embargo, si esa función es virtual, utiliza la que se implementa en la clase derivada.

  • Las funciones virtuales deben tener una definición en la clase base y también en la clase derivada, pero no son necesarias, por ejemplo, la función ToString () o toString () es Virtual para que pueda proporcionar su propia implementación anulándola en clase (s) definida por el usuario.

  • Las funciones virtuales se declaran y definen en la clase normal.

  • La función virtual pura debe declararse terminando con “= 0” y solo se puede declarar en clase abstracta.

  • Una clase abstracta que tenga una (s) función (es) virtual (es) pura (s) no puede tener una (s) definición (es) de esa función virtual pura, por lo que implica que la implementación debe proporcionarse en clase (s) derivada de esa clase abstracta.

Una función virtual es una función miembro que se declara en una clase base y que se redefine por clase derivada. La función virtual es jerárquica en orden de herencia. Cuando una clase derivada no anula una función virtual, se utiliza la función definida dentro de su clase base.

Una función virtual pura es aquella que no contiene ninguna definición relativa a la clase base. No tiene implementación en la clase base. Cualquier clase derivada debe anular esta función.