Heredar interfaces que comparten un nombre de método

Hay dos clases base que tienen el mismo nombre de función. Quiero heredar ambos, y sobre montar cada método de manera diferente. ¿Cómo puedo hacer eso con una statement y definición por separado (en lugar de definir en la definición de la clase)?

#include  class Interface1{ public: virtual void Name() = 0; }; class Interface2 { public: virtual void Name() = 0; }; class RealClass: public Interface1, public Interface2 { public: virtual void Interface1::Name() { printf("Interface1 OK?\n"); } virtual void Interface2::Name() { printf("Interface2 OK?\n"); } }; int main() { Interface1 *p = new RealClass(); p->Name(); Interface2 *q = reinterpret_cast(p); q->Name(); } 

No pude mover la definición en VC8. Encontré que la interfaz específica para palabras clave de Microsoft __ puede hacer este trabajo con éxito, codifique a continuación:

 #include  __interface Interface1{ virtual void Name() = 0; }; __interface Interface2 { virtual void Name() = 0; }; class RealClass: public Interface1, public Interface2 { public: virtual void Interface1::Name(); virtual void Interface2::Name(); }; void RealClass::Interface1::Name() { printf("Interface1 OK?\n"); } void RealClass::Interface2::Name() { printf("Interface2 OK?\n"); } int main() { Interface1 *p = new RealClass(); p->Name(); Interface2 *q = reinterpret_cast(p); q->Name(); } 

pero, ¿hay alguna otra forma de hacer algo más general que funcione en otros comstackdores?

Este problema no aparece muy a menudo. La solución con la que estoy familiarizado fue diseñada por Doug McIlroy y aparece en los libros de Bjarne Stroustrup (presentada tanto en Design & Evolution of C ++ sección 12.8 como en The C ++ Programming Language sección 25.6). De acuerdo con la discusión en Design & Evolution , hubo una propuesta para manejar este caso específico elegantemente, pero fue rechazado porque “los enfrentamientos de nombres probablemente no se volvieran lo suficientemente comunes como para justificar una función de idioma separada” y “no es probable que se conviertan en algo cotidiano”. trabajo para principiantes “.

No solo necesita llamar a Name() través de punteros a clases base, necesita una forma de decir qué Name() desea cuando opera en la clase derivada. La solución agrega alguna indirección:

 class Interface1{ public: virtual void Name() = 0; }; class Interface2{ public: virtual void Name() = 0; }; class Interface1_helper : public Interface1{ public: virtual void I1_Name() = 0; void Name() override { I1_Name(); } }; class Interface2_helper : public Interface2{ public: virtual void I2_Name() = 0; void Name() override { I2_Name(); } }; class RealClass: public Interface1_helper, public Interface2_helper{ public: void I1_Name() override { printf("Interface1 OK?\n"); } void I2_Name() override { printf("Interface2 OK?\n"); } }; int main() { RealClass rc; Interface1* i1 = &rc; Interface2* i2 = &rc; i1->Name(); i2->Name(); rc.I1_Name(); rc.I2_Name(); } 

No es bonito, pero la decisión fue que no es necesario a menudo.

He tenido que hacer algo como esto en el pasado, aunque en mi caso necesitaba heredar de una interfaz dos veces y poder diferenciar entre las llamadas realizadas en cada una de ellas, utilicé una plantilla para ayudarme …

Algo como esto:

 template class InterfaceHelper : public MyInterface { public : virtual void Name() { Name(id); } virtual void Name( const size_t id) = 0; } 

A continuación, deriva dos veces InterfaceHelper lugar de MyInterface y especifica una id diferente para cada clase base. A continuación, puede distribuir dos interfaces de forma independiente mediante conversión al InterfaceHelper correcto.

Podrías hacer algo un poco más complejo;

 class InterfaceHelperBase { public : virtual void Name( const size_t id) = 0; } class InterfaceHelper1 : public MyInterface, protected InterfaceHelperBase { public : using InterfaceHelperBase::Name; virtual void Name() { Name(1); } } class InterfaceHelper2 : public MyInterface, protected InterfaceHelperBase { public : using InterfaceHelperBase::Name; virtual void Name() { Name(2); } } class MyClass : public InterfaceHelper1, public InterfaceHelper2 { public : virtual void Name( const size_t id) { if (id == 1) { printf("Interface 1 OK?"); } else if (id == 2) { printf("Interface 2 OK?"); } } } 

Tenga en cuenta que lo anterior no ha visto un comstackdor …

No puede anularlos por separado, debe anular ambos a la vez:

 struct Interface1 { virtual void Name() = 0; }; struct Interface2 { virtual void Name() = 0; }; struct RealClass : Interface1, Interface2 { virtual void Name(); }; // and move it out of the class definition just like any other method: void RealClass::Name() { printf("Interface1 OK?\n"); printf("Interface2 OK?\n"); } 

Puede simular anulación individual con clases base intermedias:

 struct RealClass1 : Interface1 { virtual void Name() { printf("Interface1 OK?\n"); } }; struct RealClass2 : Interface2 { virtual void Name() { printf("Interface2 OK?\n"); } }; struct RealClass : RealClass1, RealClass2 { virtual void Name() { // you must still decide what to do here, which is likely calling both: RealClass1::Name(); RealClass2::Name(); // or doing something else entirely // but note: this is the function which will be called in all cases // of *virtual dispatch* (for instances of this class), as it is the // final overrider, the above separate definition is merely // code-organization convenience } }; 

Además, estás utilizando reinterpret_cast incorrectamente, deberías tener:

 int main() { RealClass rc; // no need for dynamic allocation in this example Interface1& one = rc; one.Name(); Interface2& two = dynamic_cast(one); two.Name(); return 0; } 

Y aquí hay una reescritura con CRTP que podría ser lo que quiere (o no):

 template struct RealClass1 : Interface1 { #define self (*static_cast(this)) virtual void Name() { printf("Interface1 for %s\n", self.name.c_str()); } #undef self }; template struct RealClass2 : Interface2 { #define self (*static_cast(this)) virtual void Name() { printf("Interface2 for %s\n", self.name.c_str()); } #undef self }; struct RealClass : RealClass1, RealClass2 { std::string name; RealClass() : name("real code would have members you need to access") {} }; 

Pero tenga en cuenta que aquí no puede llamar a Name en una RealClass ahora (con despacho virtual, por ejemplo, rc.Name() ), primero debe seleccionar una base. El auto macro es una manera fácil de limpiar los moldes de CRTP (generalmente el acceso de los miembros es mucho más común en la base de CRTP), pero se puede mejorar . Hay una breve discusión del despacho virtual en una de mis otras respuestas , pero seguramente una mejor si alguien tiene un enlace.

 class BaseX { public: virtual void fun() { cout < < "BaseX::fun\n"; } }; class BaseY { public: virtual void fun() { cout << "BaseY::fun\n"; } }; class DerivedX : protected BaseX { public: virtual void funX() { BaseX::fun(); } }; class DerivedY : protected BaseY { public: virtual void funY() { BaseY::fun(); } }; class DerivedXY : public DerivedX, public DerivedY { }; 

Hay otras dos preguntas relacionadas que preguntan casi (pero no completamente) cosas idénticas:

Elegir nombres heredados de métodos compartidos . Si desea tener rc.name () llame a ic1-> name () o ic2-> name ().

Anulando nombres de métodos compartidos de clases base (con plantilla) . Esto tiene una syntax más simple y menos código que la solución aceptada, pero no permite el acceso a las funciones de la clase derivada. Más o menos, a menos que necesite poder llamar a name_i1 () desde un rc, no necesita usar cosas como InterfaceHelper.