son - C++: ¿Derivado+clase base implementa una sola interfaz?
que es private en c++ (5)
En C ++, ¿es posible tener una base más una clase derivada que implemente una sola interfaz?
Por ejemplo:
class Interface
{
public:
virtual void BaseFunction() = 0;
virtual void DerivedFunction() = 0;
};
class Base
{
public:
virtual void BaseFunction(){}
};
class Derived : public Base, public Interface
{
public:
void DerivedFunction(){}
};
void main()
{
Derived derived;
}
Esto falla porque no se puede crear una instancia de Derived. En lo que respecta al compilador, Interface :: BaseFunction nunca se define.
Hasta ahora, la única solución que he encontrado sería declarar una función de transferencia en Derivado
class Derived : public Base, public Interface
{
public:
void DerivedFunction(){}
void BaseFunction(){ Base::BaseFunction(); }
};
¿Hay alguna solución mejor?
EDITAR: Si importa, aquí hay un problema del mundo real que tuve al usar cuadros de diálogo MFC.
Tengo una clase de diálogo (MyDialog digamos) que se deriva de CDialog. Debido a problemas de dependencia, necesito crear una interfaz abstracta (MyDialogInterface). La clase que usa MyDialogInterface necesita usar los métodos específicos de MyDialog, pero también necesita llamar a CDialog :: SetParent. Lo resolví creando MyDialog :: SetParent y haciéndolo pasar a CDialog :: SetParent, pero me preguntaba si habría una forma mejor.
El problema es que con su ejemplo, tiene dos implementaciones de Interface
, la que viene de Base
y la que viene de Derived
. Esto es por diseño en el lenguaje C ++. Como ya se señaló, simplemente elimine la clase base de la Interface
en la definición de Derived
.
Estoy de acuerdo con la respuesta por litb. Sin embargo, aquí hay una oportunidad para entender algo sobre cómo funcionan las funciones virtuales y la herencia múltiple.
Cuando una clase tiene múltiples clases base, tiene tablas virtuales separadas para cada clase base. Derived
tendrá una estructura vtable que se ve así:
Derived
vtable: Interface
BaseFunction*
DerivedFunction*
vtable: Base
BaseFunction*
Además, cada clase base solo podrá ver su propia tabla virtual. Cuando Base
se crea una instancia, rellena el puntero Base::BaseFunction
en el vtable, pero no puede ver el vtable para Interface.
Si el código que proporcionó pudiera compilarse, la estructura vtable resultante de una instancia de Derived
se vería así:
Derived
vtable: Interface
BaseFunction* = 0
DerivedFunction* = Derived::DerivedFunction
vtable: Base
BaseFunction* = Base::BaseFunction
Parece que no es exactamente el caso de que Derived "is-a" Base, lo que sugiere que la contención puede ser una mejor implementación que la herencia.
Además, las funciones de miembros derivados también se deben calificar como virtuales.
class Contained
{
public:
void containedFunction() {}
};
class Derived
{
public:
virtual void derivedFunction() {}
virtual void containedFunction() {return contained.containedFunction();}
private:
Containted contained;
};
Puede hacer que el miembro contenido sea una referencia o un puntero inteligente si desea ocultar los detalles de implementación.
C ++ no nota que la función heredada de Base ya implementa BaseFunction
: la función debe implementarse explícitamente en una clase derivada de Interface
. Cambiarlo de esta manera:
class Interface
{
public:
virtual void BaseFunction() = 0;
virtual void DerivedFunction() = 0;
};
class Base : public Interface
{
public:
virtual void BaseFunction(){}
};
class Derived : public Base
{
public:
virtual void DerivedFunction(){}
};
int main()
{
Derived derived;
}
Si quieres ser capaz de salirte con la suya solo implementando uno de ellos, Interface
en dos interfaces:
class DerivedInterface
{
public:
virtual void DerivedFunction() = 0;
};
class BaseInterface
{
public:
virtual void BaseFunction() = 0;
};
class Base : public BaseInterface
{
public:
virtual void BaseFunction(){}
};
class Derived : public DerivedInterface
{
public:
virtual void DerivedFunction(){}
};
class Both : public DerivedInterface, public Base {
public:
virtual void DerivedFunction(){}
};
int main()
{
Derived derived;
Base base;
Both both;
}
Nota: main debe devolver int
Nota: es una buena práctica mantenerse virtual
frente a las funciones miembro en los derivados que eran virtuales en la base, incluso si no es estrictamente necesario.
Encontré una cosa que falta en la respuesta de Litb. Si tengo una instancia Derived
, puedo obtener una DerivedInterface
y BaseInterface
. Pero si solo tengo una DerivedInterface
no puedo obtener una BaseInterface
porque derivar DerivedInterface
de BaseInterface
no funcionará.
Pero, todo este tiempo, me he estado limitando a recopilar el tiempo comprobando por alguna razón. Esta DerivedInterface
funciona muy bien:
class DerivedInterface
{
public:
virtual void DerivedFunction() = 0;
BaseInterface* GetBaseInterface()
{return dynamic_cast<BaseInterface*>(this);}
};
void main()
{
Derived derived;
DerivedInterface* derivedInterface = &derived;
derivedInterface->GetBaseInterface()->BaseFunction();
}
No se pasan las funciones necesarias en Derived, y todos están felices. Claro, ya no es estrictamente una interfaz, pero está bien. ¿Por qué no pensé en eso antes? :)