2008-11-14 23 views
12

En C++, ¿es posible tener una base más una clase derivada que implemente una única interfaz?C++: Derivado + clase base ¿implementar 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; 
} 

Esta falla porque Derivado no puede ser instanciada. En lo que respecta al compilador, Interface :: BaseFunction nunca se define.

Hasta ahora la única solución que he encontrado sería declarar un pase a través de la función derivada

class Derived : public Base, public Interface 
{ 
    public: 
     void DerivedFunction(){} 
     void BaseFunction(){ Base::BaseFunction(); } 
}; 

¿Hay alguna solución mejor?


EDIT: si importa, aquí es un problema del mundo real que había utilizando cuadros de diálogo MFC.

Tengo una clase de diálogo (MyDialog dice) 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.

Respuesta

16

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. Cambiar 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 usted quiere ser capaz de salirse con la única aplicación de uno de ellos, divididos 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: principal debe volver int
Nota: es una buena práctica mantener virtual delante de las funciones miembro en los derivados que eran virtuales en la base, incluso si no es estrictamente necesario.

+0

Si lo hace de esa manera, entonces no necesita la segunda declaración de BaseFunction. – Torlack

+0

Es lo que escribió. No quería cambiarlo. Él podría tener una razón para digerir así –

+0

. He hecho un comentario sobre eso. gracias :) –

1

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 Interface en la definición de Derived.

+0

um ... no .... Comprueba el código nuevamente. La base no hereda de la interfaz. (La interfaz tiene DerviceFunction que no está implementada en Base) –

4

Parece que no es exactamente el caso 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 la implementación.

1

Acepto 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 por separado para cada clase base.Derived tendrá una estructura vtable que tiene este aspecto:

Derived 
vtable: Interface 
    BaseFunction* 
    DerivedFunction* 
vtable: Base 
    BaseFunction* 

Además, cada clase base sólo será capaz de ver su propia vtable. Cuando se crea una instancia de Base, rellena el puntero Base::BaseFunction en el vtable, pero no puede ver el vtable para Interface.

Si el código que ya ha proporcionado puede compilar, la estructura resultante vtable de una instancia de Derived se vería así:

Derived 
vtable: Interface 
    BaseFunction* = 0 
    DerivedFunction* = Derived::DerivedFunction 
vtable: Base 
    BaseFunction* = Base::BaseFunction 
0

He encontrado una cosa que carece de la respuesta de litb. Si tengo una instancia de Derived, puedo obtener un DerivedInterface y BaseInterface. Pero si solo tengo un DerivedInterface no puedo obtener un BaseInterface ya que derivar DerivedInterface desde BaseInterface no funcionará.

Pero, todo este tiempo me he estado limitando a recopilar el tiempo comprobando por alguna razón. Este DerivedInterface funciona simplemente genial:

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 pase a través de las funciones necesarias en el derivado, y todo el mundo es feliz. Claro, ya no es estrictamente una interfaz, pero está bien. ¿Por qué no pensé en eso antes? :)

+0

eso no es problema. usted simplemente hace una clase derivada de DerivedInterface y BaseInterface, y luego tiene exactamente la interfaz de su clase "Interface" en su pregunta original –

+1

de todos modos. nunca use dynamic_cast como ese :) dynamic_cast debería ser un último recurso para solucionar lo que no se puede hacer mediante el diseño de interfaces correctas. en su caso, solo haga la interfaz como dije en el comentario anterior, y luego puede hacer BaseInterface * baseInterface = & derived; y llame a ambos :) –

+0

Sí, eso me dará la misma interfaz, pero una implementación diferente, que es el punto. Tengo la implementación para una clase base y derivada, y necesito una única interfaz para ellos.Parece que esto es lo más cercano que obtendré. – Joe

Cuestiones relacionadas