¿Cómo configuro una clase que representa una interfaz? ¿Es esto solo una clase base abstracta?
c++
inheritance
interface
abstract-class
pure-virtual
Aaron Fischer
fuente
fuente
Respuestas:
Para ampliar la respuesta de bradtgmurray , es posible que desee hacer una excepción a la lista de métodos virtuales puros de su interfaz agregando un destructor virtual. Esto le permite pasar la propiedad del puntero a otra parte sin exponer la clase derivada concreta. El destructor no tiene que hacer nada, porque la interfaz no tiene miembros concretos. Puede parecer contradictorio definir una función tanto virtual como en línea, pero confía en mí, no lo es.
No tiene que incluir un cuerpo para el destructor virtual: resulta que algunos compiladores tienen problemas para optimizar un destructor vacío y es mejor usar el predeterminado.
fuente
=0
destructor virtual ( ) puro con un cuerpo. La ventaja aquí es que el compilador puede, en teoría, ver que vtable no tiene miembros válidos ahora y descartarlo por completo. Con un destructor virtual con un cuerpo, dicho destructor se puede llamar (virtualmente), por ejemplo, en el medio de la construcción mediante unthis
puntero (cuando el objeto construido todavía es deParent
tipo), y por lo tanto el compilador debe proporcionar una tabla válida. Entonces, si no llama explícitamente a los destructores virtualesthis
durante la construcción :), puede ahorrar en el tamaño del código.override
palabra clave para permitir el argumento en tiempo de compilación y la comprobación del tipo de valor de retorno. Por ejemplo, en la declaración de Niñovirtual void OverrideMe() override;
Haz una clase con métodos virtuales puros. Use la interfaz creando otra clase que anule esos métodos virtuales.
Un método virtual puro es un método de clase que se define como virtual y se asigna a 0.
fuente
override
en C ++ 11Toda la razón por la que tiene una categoría de tipo de interfaz especial además de las clases base abstractas en C # / Java es porque C # / Java no admite la herencia múltiple.
C ++ admite herencia múltiple, por lo que no se necesita un tipo especial. Una clase base abstracta sin métodos no abstractos (virtuales puros) es funcionalmente equivalente a una interfaz C # / Java.
fuente
Thread
instancia. La herencia múltiple puede ser un mal diseño, así como la composición. Todo depende del caso.No existe el concepto de "interfaz" per se en C ++. AFAIK, las interfaces se introdujeron por primera vez en Java para evitar la falta de herencia múltiple. Este concepto ha resultado ser bastante útil, y se puede lograr el mismo efecto en C ++ mediante el uso de una clase base abstracta.
Una clase base abstracta es una clase en la que al menos una función miembro (método en jerga de Java) es una función virtual pura declarada utilizando la siguiente sintaxis:
No se puede crear una instancia de una clase base abstracta, es decir, no puede declarar un objeto de la clase A. Solo puede derivar clases de A, pero cualquier clase derivada que no proporcione una implementación de
foo()
también será abstracta. Para dejar de ser abstracto, una clase derivada debe proporcionar implementaciones para todas las funciones virtuales puras que hereda.Tenga en cuenta que una clase base abstracta puede ser más que una interfaz, ya que puede contener miembros de datos y funciones de miembros que no son virtuales. Un equivalente de una interfaz sería una clase base abstracta sin ningún dato con solo funciones virtuales puras.
Y, como Mark Ransom señaló, una clase base abstracta debería proporcionar un destructor virtual, al igual que cualquier clase base, para el caso.
fuente
Hasta donde pude probar, es muy importante agregar el destructor virtual. Estoy usando objetos creados con
new
y destruidos condelete
.Si no agrega el destructor virtual en la interfaz, no se llama al destructor de la clase heredada.
Si ejecuta el código anterior sin
virtual ~IBase() {};
, verá queTester::~Tester()
nunca se llama al destructor .fuente
Mi respuesta es básicamente la misma que las otras, pero creo que hay otras dos cosas importantes que hacer:
Declare un destructor virtual en su interfaz o cree uno no virtual protegido para evitar comportamientos indefinidos si alguien intenta eliminar un objeto de tipo
IDemo
.Use la herencia virtual para evitar problemas con la herencia múltiple. (Con mayor frecuencia hay herencia múltiple cuando usamos interfaces).
Y como otras respuestas:
Use la interfaz creando otra clase que anule esos métodos virtuales.
O
Y
fuente
En C ++ 11 puede evitar fácilmente la herencia por completo:
En este caso, una interfaz tiene semántica de referencia, es decir, debe asegurarse de que el objeto sobreviva a la interfaz (también es posible hacer interfaces con semántica de valor).
Este tipo de interfaces tienen sus pros y sus contras:
Finalmente, la herencia es la raíz de todo mal en el diseño de software complejo. En la semántica de valor de Sean Parent y el polimorfismo basado en conceptos (muy recomendable, se explican mejores versiones de esta técnica), se estudia el siguiente caso:
Digamos que tengo una aplicación en la que trato mis formas polimórficamente usando la
MyShape
interfaz:En su aplicación, hace lo mismo con diferentes formas utilizando la
YourShape
interfaz:Ahora digamos que desea usar algunas de las formas que he desarrollado en su aplicación. Conceptualmente, nuestras formas tienen la misma interfaz, pero para que mis formas funcionen en su aplicación, necesitaría extender mis formas de la siguiente manera:
Primero, modificar mis formas podría no ser posible en absoluto. Además, la herencia múltiple conduce al camino hacia el código de espagueti (imagine que viene un tercer proyecto que usa el
TheirShape
interfaz ... ¿qué sucede si también llaman a su función de dibujomy_draw
?).Actualización: Hay un par de nuevas referencias sobre el polimorfismo basado en la no herencia:
fuente
Circle
clase es un diseño pobre. Debe usar elAdapter
patrón en tales casos. Perdón si suena un poco duro, pero trata de usar alguna biblioteca de la vida real comoQt
antes de hacer juicios sobre la herencia. La herencia hace la vida mucho más fácil.Adapter
patrón? Estoy interesado en ver sus ventajas.Square
que ya no está allí? ¿Presciencia? Por eso está separado de la realidad. Y, en realidad, si elige confiar en la biblioteca "MyShape", puede adoptar su interfaz desde el principio. En el ejemplo de formas hay muchas tonterías (una de las cuales es que tiene dosCircle
estructuras), pero el adaptador se vería así -> ideone.com/UogjWkTodas las buenas respuestas anteriores. Una cosa adicional que debe tener en cuenta: también puede tener un destructor virtual puro. La única diferencia es que aún necesita implementarlo.
¿Confuso?
La razón principal por la que desearía hacer esto es si desea proporcionar métodos de interfaz, como yo lo he hecho, pero hacer que la anulación sea opcional.
Para convertir la clase en una clase de interfaz se requiere un método virtual puro, pero todos sus métodos virtuales tienen implementaciones predeterminadas, por lo que el único método que queda para hacer virtual puro es el destructor.
Reimplementar un destructor en la clase derivada no es gran cosa, siempre reimplemento un destructor, virtual o no, en mis clases derivadas.
fuente
Si está utilizando el compilador C ++ de Microsoft, puede hacer lo siguiente:
Me gusta este enfoque porque da como resultado un código de interfaz mucho más pequeño y el tamaño del código generado puede ser significativamente más pequeño. El uso de novtable elimina toda referencia al puntero vtable en esa clase, por lo que nunca puede crear una instancia directamente. Vea la documentación aquí - novtable .
fuente
novtable
más de lo estándarvirtual void Bar() = 0;
= 0;
que falta que he agregado). Lea la documentación si no la comprende.= 0;
y asumí que era solo una forma no estándar de hacer exactamente lo mismo.Una pequeña adición a lo que está escrito allí:
Primero, asegúrese de que su destructor también sea puramente virtual
En segundo lugar, es posible que desee heredar virtualmente (en lugar de normalmente) cuando implemente, solo por buenas medidas.
fuente
También puede considerar las clases de contrato implementadas con el NVI (Patrón de interfaz no virtual). Por ejemplo:
fuente
Todavía soy nuevo en el desarrollo de C ++. Empecé con Visual Studio (VS).
Sin embargo, nadie parece mencionar el
__interface
en VS (.NET) . Soy no muy seguro de si esto es una buena manera de declarar una interfaz. Pero parece proporcionar una aplicación adicional (mencionada en los documentos ). De modo que no tenga que especificar explícitamente elvirtual TYPE Method() = 0;
, ya que se convertirá automáticamente.Si alguien tiene algo interesante al respecto, compártelo. :-)
Gracias.
fuente
Si bien es cierto que
virtual
es el estándar de facto para definir una interfaz, no olvidemos el clásico patrón tipo C, que viene con un constructor en C ++:Esto tiene la ventaja de que puede volver a vincular el tiempo de ejecución de los eventos sin tener que construir su clase nuevamente (como C ++ no tiene una sintaxis para cambiar los tipos polimórficos, esta es una solución alternativa para las clases de camaleón).
Consejos:
click
el constructor de su descendiente.protected
miembro y tener unpublic
referencia y / o captador.if
cambios de s vs. estado en su código, esto podría ser más rápido queswitch()
es oif
s (se espera un cambio de alrededor de 3-4if
s, pero siempre mida primero).std::function<>
sobre punteros de función, es posible que pueda administrar todos los datos de sus objetosIBase
. Desde este punto, puede tener esquemas de valor paraIBase
(por ejemplo,std::vector<IBase>
funcionará). Tenga en cuenta que esto puede ser más lento dependiendo de su compilador y código STL; también que las implementaciones actualesstd::function<>
tienden a tener una sobrecarga en comparación con los punteros de función o incluso las funciones virtuales (esto podría cambiar en el futuro).fuente
Aquí está la definición de
abstract class
en estándar c ++n4687
13.4.2
fuente
Resultado: Área del rectángulo: 35 Área del triángulo: 17
Hemos visto cómo una clase abstracta definió una interfaz en términos de getArea () y otras dos clases implementaron la misma función pero con un algoritmo diferente para calcular el área específica de la forma.
fuente