繁体   English   中英

C ++中多接口继承的歧义

[英]Ambiguity in multiple inheritance of interfaces in C++

我制作了如下测试代码:

#include <iostream>
using namespace std;

#ifndef interface
#define interface struct
#endif

interface Base
{
    virtual void funcBase() = 0;
};

interface Derived1 : public Base
{
    virtual void funcDerived1() = 0;
};

interface Derived2 : public Base
{
    virtual void funcDerived2() = 0;
};

interface DDerived : public Derived1, public Derived2
{
    virtual void funcDDerived() = 0;
};

class Implementation : public DDerived
{
public:
    void funcBase() { cout << "base" << endl; }
    void funcDerived1() { cout << "derived1" << endl; }
    void funcDerived2() { cout << "derived2" << endl; }
    void funcDDerived() { cout << "dderived" << endl; }
};

int main()
{
    DDerived *pObject = new Implementation;
    pObject->funcBase();

    return 0;
}

我编写此代码的原因是测试函数funcBase()是否可以在DDerived实例中调用。 当我尝试编译此代码时,我的C ++编译器(Visual Studio 2010)给了我一个编译错误消息。 在我看来,这段代码没有问题,因为可以确定函数funcBase()将在DDerived接口的某个派生类中实现(因此覆盖),因为它是纯虚拟的。 换句话说,任何类型为Implementation *指针变量都应该与派生Implentation并覆盖函数funcBase()的类的实例相关联。

我的问题是,为什么编译器会给我这样的错误信息? 为什么C ++语法是这样定义的; 即,将此案件视为错误? 如何让代码运行? 我想允许接口的多重继承。 当然,如果我使用“虚拟公共”或在Implementation重新声明函数funcBase()

interface DDerived : public Derived1, public Derived2
{
    virtual void funcBase() = 0;
    virtual void funcDDerived() = 0;
};

然后一切都运行没有问题。

但我不想这样做并寻找更方便的方法,因为虚拟继承可能会降低性能,如果类的继承关系非常复杂,则重新声明是如此繁琐。 除了使用虚拟继承之外,是否有任何方法可以在C ++中启用接口的多重继承?

正如您所定义的那样,您的对象结构如下所示:

在此输入图像描述

这里重要的一点是,每个Implementation实例都包含两个完全独立的Base实例。 你提供的替代Base::funcBase ,但它不知道你是否尝试覆盖funcBaseBase ,你通过继承Derived1Base ,你通过继承Derived2

是的,处理这个问题的干净方法是虚拟继承。 这将改变你的结构,所以只有一个Base实例:

在此输入图像描述

这几乎无疑是你真正想要的。 是的,它在原始编译器和25 MHz 486等时代因性能问题而闻名。 使用现代编译器和处理器,您不太可能遇到问题。

另一种可能性是某种基于模板的替代方案,但这往往遍及代码的其余部分 - 即,不是传递Base * ,而是编写一个模板,该模板将与提供函数A,B和C,然后传递(相当于) Implementation作为模板参数。

C ++语言的设计方式是,在没有虚拟继承的第一种方法中,将有两个方法的父副本,它无法确定要调用哪一个。

虚拟继承是从多个基础继承相同函数的C ++解决方案,因此我建议使用该方法。

或者你认为不从多个基地继承相同的功能? 您是否真的有一个派生类,您需要能够根据上下文将其视为Derived1Derived2 OR Base

在这种情况下,详细阐述具体问题而不是人为的例子可能有助于提供更好的设计。

DDerived *pObject = new Implementation;
pObject->funcBase();

这将创建一个DDerived类型的指针到实现。 当你使用DDerived时,你真的只有一个指向接口的指针。

DDerived不知道funcBase的实现,因为在Derived1和Derived2中都定义了funcBase的模糊性。

这创造了一个继承钻石,这是真正导致问题的原因。

http://en.wikipedia.org/wiki/Diamond_problem

我还必须检查你在那里的“关键字”界面

它是视觉工作室认可的特定于ms的扩展

我认为C ++ Standard 10.1.4 - 10.1.5可以帮助您理解代码中的问题。

class L { public: int next; /∗ ... ∗/ };
class A : public L { /∗...∗/ };
class B : public L { /∗...∗/ };
class C : public A, public B { void f(); /∗ ... ∗/ };

10.1.4 包含关键字virtual的基类说明符指定非虚基类。 包含关键字virtual的基类说明符指定虚拟基类。 对于最派生类的类网格中非虚拟基类的每个不同出现,最派生对象(1.8)应包含该类型的相应不同基类子对象。 对于指定为virtual的每个不同的基类,最派生的对象应包含该类型的单个基类子对象。 [ 示例:对于类类型C的对象,C类的类中的每个不同出现的(非虚拟)基类L与类型C的对象内的不同L子对象一一对应。给定上面定义的C类,C类的对象将有两个L类子对象,如下所示。

10.1.5在这种格子中,可以使用显式限定来指定哪个子对象。 函数C :: f的主体可以引用每个L子对象的下一个成员:void C :: f(){A :: next = B :: next; } //形式良好 如果没有A ::或B ::限定符, 由于模糊性,上面C :: f的定义将是不正确的

所以只需在调用pObject-> funcBase()时添加限定符或以另一种方式解决歧义。

pObject->Derived1::funcBase();

更新:同样非常有帮助的阅读将是10.3标准的虚拟功能

周末愉快 :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM