? Sé que está bien que una función virtual pura tenga una implementación. Sin embargo, ¿por qué es así? ¿Hay conflicto por los dos conceptos? ¿Cuál es el uso? ¿Alguien puede ofrecer algún ejemplo?¿Cuál es el uso si proporciono una implementación para una función virtual pura en C++
Respuesta
se abordó en GotW #31. Resumen:
Hay tres razones principales por las que puede hacer esto. # 1 es un lugar común, # 2 es bastante raro, y # 3 es una solución alternativa utilizada ocasionalmente por los programadores avanzados que trabajan con compiladores más débiles .
La mayoría de los programadores solo deben usar el n. ° 1.
... Que es para destructores virtuales puros.
¡Bonito hallazgo! +1 FTW –
No estoy de acuerdo con 2 como bastante raro. Prefiero obligar al desarrollador a declarar explícitamente que desea usar el valor predeterminado. Demasiados se desarrollan compilando y se detienen tan pronto como lo hace, al menos de esta manera tengo la esperanza de que puedan leer el fragmento para que sepan lo que está haciendo el valor predeterminado. –
De esta manera puede proporcionar una implementación que funcione, pero aún así requiere que el implementador de clase hijo llame explícitamente a esa implementación.
Wikipedia resume esto muy bien:
Aunque los métodos virtuales puros normalmente no tienen aplicación en la clase que los declara, métodos virtuales puros en C++ están permitidos para contener una implementación en su declarando la clase, proporcionando el comportamiento predeterminado de Fallback o al que puede delegar una clase derivada , si corresponde.
Normalmente no necesita proporcionar implementaciones de la clase base para virtuales puros. Pero hay una excepción: pure virtual destructors. De hecho, si su clase base tiene un destructor virtual puro, it must have an implementation. ¿Por qué necesitarías un destructor virtual puro en lugar de uno virtual? Típicamente, para hacer una clase base abstracta sin requerir la implementación de ningún otro método. Por ejemplo, en una clase en la que puede razonablemente usar la implementación predeterminada para cualquier método, pero todavía no quiere que las personas creen una instancia de la clase base, puede marcar solo el destructor como puramente virtual.
EDIT:
Aquí hay algo de código que ilustra algunas de las formas para llamar a la implementación base:
#include <iostream>
using namespace std;
class Base
{
public:
virtual void DoIt() = 0;
};
class Der : public Base
{
public:
void DoIt();
};
void Base::DoIt()
{
cout << "Base" << endl;
}
void Der::DoIt()
{
cout << "Der" << endl;
Base::DoIt();
}
int main()
{
Der d;
Base* b = &d;
d.DoIt();
b->DoIt(); // note that Der::DoIt is still called
b->Base::DoIt();
return 0;
}
Hace _prevenir_ evitar que se cree una instancia de la clase base. – KitsuneYMG
@kts: sí, por supuesto que sí. ¿Dije que no? –
No hay conflicto con los dos conceptos, aunque rara vez se utilizan juntos (como se puristas OO no puede conciliarlo, pero eso está más allá del alcance de esta pregunta/respuesta).
La idea es que la función pure virtual tenga una implementación y, al mismo tiempo, obligue a las subclases a anular esa implementación. Las subclases pueden invocar la función de clase base para proporcionar algún comportamiento predeterminado. La base no se puede instanciar (es "abstracta") porque la (s) función (es) virtual (es) es pura aunque pueda tener una implementación.
@STingRaySC: Gracias por aclararme esto. Siempre pensé que una función virtual pura no podría tener una implementación. pero realmente solo dice que un tipo derivado debe anularlo.Y ahora puedo ver por qué mi respuesta no respondió la pregunta de OP. Perdón por mi testarudez al entender esta distinción. –
@Brian: sin preocupaciones. Es algo poco conocido. Me alegra que finalmente lo hayas conseguido. –
Bueno, tenemos algunas buenas respuestas ya .. Estoy a frenar en la escritura ..
Mi pensamiento sería, por ejemplo, una función init que tiene try {} catch {}, lo que significa que shouldn 't ser colocado en un constructor:
class A {
public:
virtual bool init() = 0 {
... // initiate stuff that couldn't be made in constructor
}
};
class B : public A{
public:
bool init(){
...
A::init();
}
};
En Efectivo C++, Scott Meyers da el ejemplo de que es útil cuando se reutiliza el código a través de la herencia. Empieza con esto:
struct Airplane {
virtual void fly() {
// fly the plane
}
...
};
struct ModelA : Airplane { ... };
struct ModelB : Airplane { ... };
Ahora, ModelA y ModelB se vuelan la misma manera, y que cree que es una forma común de volar un avión, por lo que el código está en la clase base. Sin embargo, no todos los aviones se vuelan de esa manera, y tenemos la intención de que los planos sean polimórficos, por lo que es virtual.
Ahora añadimos ModelC, que debe ser trasladado de manera diferente, pero comete un error:
struct ModelC : Airplane { ... (no fly function) };
Vaya. ModelC va a estrellarse. Meyers preferiría que el compilador nos advirtiera de nuestro error.
Por lo tanto, se hace fly
pura virtual en avión con una aplicación, y luego en ModelA y ModelB, poner:
void fly() { Airplane::fly(); }
Ahora, a menos que explicitamente estado en nuestra clase derivada que queremos que el vuelo predeterminado comportamiento, no lo conseguimos. Entonces, en lugar de solo la documentación que nos dice todas las cosas que debemos verificar sobre nuestro nuevo modelo de avión, el compilador también nos dice.
Esto hace el trabajo, pero creo que es un poco débil. Idealmente, tenemos una mezcla de BoringlyFlyable que contiene la implementación predeterminada de fly, y reutilizamos el código de esa manera, en lugar de poner código en una clase base que asume ciertas cosas sobre los aviones que no son requisitos de los aviones. Pero eso requiere CRTP si la función fly
realmente hace algo significativo:
#include <iostream>
struct Wings {
void flap() { std::cout << "flapping\n"; }
};
struct Airplane {
Wings wings;
virtual void fly() = 0;
};
template <typename T>
struct BoringlyFlyable {
void fly() {
// planes fly by flapping their wings, right? Same as birds?
// (This code may need tweaking after consulting the domain expert)
static_cast<T*>(this)->wings.flap();
}
};
struct PlaneA : Airplane, BoringlyFlyable<PlaneA> {
void fly() { BoringlyFlyable<PlaneA>::fly(); }
};
int main() {
PlaneA p;
p.fly();
}
Cuando Planea declara herencia de BoringlyFlyable, que hace valer a través de interfaz que es válida para volar en la forma predeterminada. Tenga en cuenta que BoringlyFlyable podría definir funciones virtuales puras propias: quizás getWings
sería una buena abstracción. Pero como es una plantilla, no es necesario.
Tengo la sensación de que este patrón puede reemplazar todos los casos en los que hubiera proporcionado una función virtual pura con una implementación: la implementación puede ir en un mixin, que las clases pueden heredar si lo desean. Pero no puedo probar de inmediato que (por ejemplo, si Airplane::fly
utiliza miembros privados, entonces se requiere un rediseño considerable para hacerlo de esta manera), y podría decirse que CRTP es un poco de alta potencia para el principiante de todos modos. También hay un poco más de código que no agrega funcionalidad o seguridad, simplemente hace explícito lo que ya está implícito en el diseño de Meyer, que algunas cosas pueden volar simplemente batiendo sus alas mientras que otras necesitan hacer otras cosas. Entonces mi versión de ninguna manera es un shoo-in total.
Tanto como las plantillas similares, forzar la herencia múltiple en nosotros parece bastante molesto. ¿Por qué no simplemente proporcionar una función de plantilla? 'plantilla
Es solo que las mezclas no funcionan tan bien con las funciones virtuales como te gustaría. Una opción es hacer que BoringlyFlyable herede Airplane (quizás como un parámetro de plantilla, para mantenerlo flexible), por lo que PlaneA no hereda directamente de Airplane, solo a través de 'BoringlyFlyable
- 1. ¿Cuál es el sentido de una función virtual pura privada?
- 2. diferencia entre una función virtual y una función virtual pura
- 3. ¿Cuál es el equivalente de una función pura virtual de C++ en Objective-C?
- 4. ¿En qué circunstancias es ventajoso dar una implementación de una función virtual pura?
- 5. función virtual pura C++ tienen cuerpo
- 6. En C++, ¿es una función automáticamente virtual si anula una función virtual?
- 7. Función virtual pura llamada error
- 8. Cómo implementar la función virtual pura en C++
- 9. ¿Cómo proporciono una implementación predeterminada para un protocolo Objective-C?
- 10. Llamada de función virtual pura
- 11. ¿Se utiliza una función de miembro virtual si no es pura?
- 12. ¿Hay alguna manera de "eliminar" una función virtual pura?
- 13. ¿Por qué numCapabilities es una función pura?
- 14. ¿Dónde se encuentra la función virtual pura en C++?
- 15. Formas de detectar si una función virtual C++ ha sido redefinida en una clase derivada
- 16. Implementación pura de la función virtual de C++ y archivos de encabezado
- 17. redefinir una función no virtual en C++
- 18. ¿Qué puede causar una llamada de función virtual pura en C++?
- 19. Pregunta de clase virtual pura de C++
- 20. En C++, ¿es posible tener una función puramente virtual definida?
- 21. ¿Qué tan útil es una implementación de MVC "pura"?
- 22. clase de Padres C++ llamar a una función virtual niño
- 23. ¿Hay alguna diferencia entre una función virtual pura privada y protegida?
- 24. ¿Cómo sabe el compilador de C++ qué implementación de una función virtual llamar?
- 25. ¿Una función virtual anula una función no virtual del mismo nombre en una clase base?
- 26. Captura de llamada virtual pura R6025
- 27. Averiguar si una propiedad es declarada virtual
- 28. Lista de entradas a una función pura
- 29. ¿Es posible determinar/afirmar que si una función virtual queda anulada, otra también se reemplaza?
- 30. ¿Cada función abstracta es virtual en C#, en general?
¿Y los dos conceptos son ...? – Sam