inheritance共享方法名称的接口
有两个基类具有相同的函数名称。 我想inheritance他们两个,并以不同的方式骑在每个方法。 我怎样才能做到这一点与单独的声明和定义(而不是定义类定义)?
#include <cstdio> 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<RealClass*>(p); q->Name(); }
我没有把这个定义移出VC8。 我发现Microsoft Specific Keyword __interface可以成功完成这项工作,代码如下:
#include <cstdio> __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<RealClass*>(p); q->Name(); }
但有没有另外一种方法可以做到这一点更普遍的,将在其他编译器工作?
这个问题不经常出现。 我熟悉的解决scheme是Doug McIlroydevise的,出现在Bjarne Stroustrup的书中(同时出现在C ++devise与演化第12.8节和C ++编程语言第25.6节)。 根据“ devise与进化” ( Design&Evolution)的讨论,有一个build议是优雅地处理这个具体的案例,但是它被拒绝了,因为“这样的名称冲突不可能变得普遍到足以保证单独的语言特征”,并且“不可能成为每天为新手工作“。
您不仅需要通过指向基类的指针调用Name()
,还需要一种方法来说明在派生类上操作时所需的Name()
。 该解决scheme增加了一些间接性:
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(); }
不漂亮,但决定是不经常需要的。
过去我不得不这样做,但在我的情况下,我需要从一个接口两次inheritance,并能够区分每个接口上的调用,我使用模板垫片来帮助我…
像这样的东西:
template<class id> class InterfaceHelper : public MyInterface { public : virtual void Name() { Name(id); } virtual void Name( const size_t id) = 0; }
然后从InterfaceHelper
派生两次,而不是从MyInterface
两次,然后为每个基类指定一个不同的id
。 然后,您可以通过投射到正确的InterfaceHelper
来独立地分发两个InterfaceHelper
。
你可以做一些稍微复杂的事情。
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?"); } } }
请注意,上面还没有看到一个编译器…
你不能单独覆盖它们,你必须一次覆盖它们:
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"); }
你可以用中间基类来模拟单个重写:
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 } };
另外,你正在使用reinterpret_cast不正确,你应该有:
int main() { RealClass rc; // no need for dynamic allocation in this example Interface1& one = rc; one.Name(); Interface2& two = dynamic_cast<Interface2&>(one); two.Name(); return 0; }
而这里用CRTP重写可能是你想要的(或者不是):
template<class Derived> struct RealClass1 : Interface1 { #define self (*static_cast<Derived*>(this)) virtual void Name() { printf("Interface1 for %s\n", self.name.c_str()); } #undef self }; template<class Derived> struct RealClass2 : Interface2 { #define self (*static_cast<Derived*>(this)) virtual void Name() { printf("Interface2 for %s\n", self.name.c_str()); } #undef self }; struct RealClass : RealClass1<RealClass>, RealClass2<RealClass> { std::string name; RealClass() : name("real code would have members you need to access") {} };
但是请注意,在这里您不能在RealClass上调用Name(使用虚拟调度,例如rc.Name()
),您必须先select一个基础。 selfmacros是清理CRTP强制转换的一种简单方法(通常CRTP基础中成员的访问比较普遍),但是它可以被改进 。 在我的其他答案中有一个关于虚拟调度的简短讨论,但如果有人链接的话,肯定会有更好的答案 。
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 { };
还有两个相关的问题提出了几乎(但不是完全)相同的东西:
从inheritance的共享方法名称中select 。 如果你想让rc.name()调用ic1-> name() 或 ic2-> name()。
覆盖(模板化)基类中的共享方法名称 。 这个语法更简单,代码更less,但是不允许从派生类访问函数。 或多或less,除非需要能够从rc调用name_i1(),否则不需要使用诸如InterfaceHelper之类的东西。