[英]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
,但它不知道你是否尝试覆盖funcBase
为Base
,你通过继承Derived1
或Base
,你通过继承Derived2
。
是的,处理这个问题的干净方法是虚拟继承。 这将改变你的结构,所以只有一个Base实例:
这几乎无疑是你真正想要的。 是的,它在原始编译器和25 MHz 486等时代因性能问题而闻名。 使用现代编译器和处理器,您不太可能遇到问题。
另一种可能性是某种基于模板的替代方案,但这往往遍及代码的其余部分 - 即,不是传递Base *
,而是编写一个模板,该模板将与提供函数A,B和C,然后传递(相当于) Implementation
作为模板参数。
C ++语言的设计方式是,在没有虚拟继承的第一种方法中,将有两个方法的父副本,它无法确定要调用哪一个。
虚拟继承是从多个基础继承相同函数的C ++解决方案,因此我建议使用该方法。
或者你认为不从多个基地继承相同的功能? 您是否真的有一个派生类,您需要能够根据上下文将其视为Derived1
或Derived2
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.