在 C++ 中实现多个接口

Posted

技术标签:

【中文标题】在 C++ 中实现多个接口【英文标题】:Implementing multiple interfaces in c++ 【发布时间】:2013-05-20 10:07:44 【问题描述】:

我的接口层次结构如下:

class A

public:
 void foo() = 0;
;

class B: public A

public:
void testB() = 0;
;

class C: public A

public:
void testC() = 0;
;

现在,我想通过相同的层次结构实现这些接口,即基类AImplBImplCImpl,但我不知道如何从它们对应的接口派生它们。

请帮忙。 提前致谢。

【问题讨论】:

你为什么不能class BImpl : public B ... 我不明白你的问题.... 一个帮助:make your destructors virtual 【参考方案1】:

您可以使用单独的模板实现每个单独的接口,然后链接模板以构建派生对象,就像从构建块中一样。古老的 ATL 库也使用此方法来实现 COM 接口(对于我们这些足够老的人来说)。

请注意,您不需要虚拟继承。

我稍微修改了您的示例以获得更复杂的推导 C -> B -> A,以展示此方法如何轻松扩展:

#include <stdio.h>

// Interfaces

struct A

    virtual void foo() = 0;
;

struct B : A

    virtual void testB() = 0;
;

struct C : B

    virtual void testC() = 0;
;

// Implementations

template<class I>
struct AImpl : I

    void foo()  printf("%s\n", __PRETTY_FUNCTION__); 
;

template<class I>
struct BImpl : I

    void testB()  printf("%s\n", __PRETTY_FUNCTION__); 
;

template<class I>
struct CImpl : I

    void testC()  printf("%s\n", __PRETTY_FUNCTION__); 
;


// Usage

int main() 
    // Compose derived objects from templates as from building blocks.
    AImpl<A> a;
    BImpl<AImpl<B> > b;
    CImpl<BImpl<AImpl<C> > > c;

    a.foo();

    b.foo();
    b.testB();

    c.foo();
    c.testB();
    c.testC();

输出:

void AImpl<I>::foo() [with I = A]
void AImpl<I>::foo() [with I = B]
void BImpl<I>::testB() [with I = AImpl<B>]
void AImpl<I>::foo() [with I = C]
void BImpl<I>::testB() [with I = AImpl<C>]
void CImpl<I>::testC() [with I = BImpl<AImpl<C> >]

【讨论】:

虽然我也喜欢其他答案,但我接受这个作为答案,因为它非常接近我现有的设计。【参考方案2】:

虽然在建议多重继承之前我通常会三思而后行。如果您的需求确实没有您说的那么复杂,那么只需虚拟继承接口和实现即可。

class A

public:
  void foo() = 0;
;

class B: virtual public A

public:
  void testB() = 0;
;

class C: virtual public A

public:
  void testC() = 0;
;

class AImpl : virtual public A

 ...


class BImpl : virtual public B, virtual public AImpl

 ...

虚拟继承将确保ABAImpl 之间共享一个子对象。所以你应该清楚。但是,对象大小会增加。因此,如果这是一个问题,请重新考虑您的设计。

【讨论】:

恕我直言,虚拟继承确实解决了这里的问题,但是,它以增加对象大小和运行时处理为代价。 OTOH,不用支付这些费用就可以解决这个问题。 @MaximYegorushkin,是的,在其他出色的答案中有更好的选择。但鉴于我已经 两次 发表了免责声明,我没有得到反对票。 我猜投反对票的人是根据其优点而非免责声明来判断您的解决方案的。 @MaximYegorushkin,好吧,鉴于投票者不愿意解释他或她的判断,我们永远不会知道或关心。可惜。【参考方案3】:

我认为您要问的是,如何创建反映我的“交互”类层次结构的第二个实现类层次结构。您希望这样做以减少接口和实现之间的耦合。您的问题是您认为您的实现类应该扩展您的接口类,但也应该扩展 impl 类中的“自然”继承层次结构,如

   AImpl
  ^    ^
  |    |
BImpl  CImpl

我建议您查看描述此模式的 GoF(四人帮)Bridge pattern。实际上,书中的例子比***上的要好,但似乎在网上找不到。这是一个粗略的(ascii)UML:

Window <>imp----------------------------> WindowImpl
^    ^                                    ^        ^
|    |                                    |        |
|   TransientWindow                       |     PMWindowImp
IconWindow                              XWindowImp

在上面的示例中,您有一个Window 类,其#imp 引用了实现。因此,您的IconWindow 实现将有#imp 引用,例如XWindowIconWindow 来执行实际工作,但客户端只会通过IconWindow 实例引用它。这减少了您的重新编译开销。

要说明的要点是:

窗口类型(普通窗口、图标窗口、对话框)和窗口实现(X-windows、PMWindows)的独立类层次结构。它们可以独立扩展。 操作是根据 WindowImp 上的抽象操作来实现的。因此,操作是从平台细节中抽象出来的。 如果您尝试混合抽象和多个实现,请减少类的增加。

我会让你把它翻译成你的问题,但如果你有任何问题,请发表评论。

【讨论】:

+1 在正确的行上,但可能不是完全正确的模式。问题是派生接口和派生实现是不相关的。我将 OP 解释为想要封装由派生实现实现的扩展的派生接口。为了适应 GoF 示例,我将删除 TransientWindowIconWindow 并将它们替换为 PMWindowXWindow,因此您最终会得到一个反映实现层次结构的接口层次结构。 Lakos 1996 Large Scale C++ Software Design 提出了一种可能相关的称为协议层次结构的模式。 话虽如此,如果OP的设计可以适合Bridge模式,那就更可取了;保持界面狭窄并避免类的组合爆炸。【参考方案4】:

class BImpl : public B 
    // code ...

但在 B 中,将函数声明为虚拟函数,以便编译器强制您在 BImpl 中实现它们

【讨论】:

你的意思是把它们声明为 pure virtual。

以上是关于在 C++ 中实现多个接口的主要内容,如果未能解决你的问题,请参考以下文章

在graphql中实现多个接口

为啥在具有多个接口() 的对象中实现 QueryInterface() 时我需要显式向上转换

如何在基类中实现子类迭代器的统一接口?

通用扩展类 AND 在 Kotlin 中实现接口

接口的使用,内部类

vue调用下载接口弹出保存文件窗口