我想在java或c#中使用c ++中的接口。我决定使用具有多重继承的纯抽象类,但是当我专门化接口时,某些东西是非常错误的:
class Interface
{
public:
virtual int method() = 0;
};
// Default implementation.
class Base: virtual public Interface
{
public:
virtual int method() {return 27;}
};
// specialized interface
class Interface2: public Interface
{
public:
virtual int method() = 0;
// some other methods here
};
// concrete class - not specialised - OK
class Class: public virtual Interface, public virtual Base
{
};
// concrete class - specialised
class Class2: public Interface2, public Base
{
};
int main()
{
Class c;
Class2 c2;
return 0;
}
警告1警告C4250:'Class':通过支配30继承'Base :: Base :: method'
错误2错误C2259:'Class2':无法实例化抽象类42
这样做的正确方法是什么?
答案 0 :(得分:5)
Class2继承自抽象类(Interface2),但不实现纯虚方法,因此它仍然是一个抽象类。
答案 1 :(得分:4)
基于此评论
If the method is not reimplemented in Class2 or Class (it is not in this case) Base::method() will be called. Otherwise the reimplementation will be called. There is an interface hierarchy with a common base dumb implementation. – danatel 16 mins ago
这不是你得到的,你没有共同的基础,你有
Interface -> Interface2 -> Class2
Interface -> Base -> Class2
接口在派生树中没有“合并”,interface2几乎不从接口继承,因此它将拥有自己的接口超类。
这就像纯虚拟method()
在Class2中存在两次,一旦通过Class实现,一旦未实现。
即使您已经虚拟继承,公共基础(接口)仍然没有实现
如果Base包含应该在整个层次结构中使用的简单操作,那么为什么不将Base作为起点? (即使仍然是纯虚拟的实现)。
如果这只是一个简单的例子来解决问题,那么像Bridge Pattern这样的东西可能更有用。但是如果不了解更多信息,很难引导你。
答案 2 :(得分:4)
我认为这个简单的例子显示了相同的东西,但可能更容易理解,因为它使用的东西很容易可视化:(请原谅结构懒惰)
#include <iostream>
using namespace std;
struct Vehicle
{
virtual void Drive() = 0;
};
struct VehicleImp : virtual public Vehicle
{
virtual void Drive()
{
cout << "VehicleImp::Drive\n";
}
};
struct Tank : virtual public Vehicle
{
virtual void RotateTurret() = 0;
};
struct TankImp : public Tank, public VehicleImp
{
virtual void RotateTurret()
{
cout << "TankImp::RotateTurret\n";
}
// Could override Drive if we wanted
};
int _tmain(int argc, _TCHAR* argv[])
{
TankImp myTank;
myTank.Drive(); // VehicleImp::Drive
myTank.RotateTurret(); // TankImp::RotateTurret
return 0;
}
TankImp基本上继承了Tank接口和Vehicle实现。
现在,我很确定这是OO圈中众所周知且可以接受的东西(但我不知道它是否有一个奇特的名字),所以在这种情况下可怕的钻石是好的,你可以安全地压制支配警告,因为这是你想要在这种情况下发生的事情。
希望以某种方式帮助你指明正确的方向!
BTW,你的代码没有编译,因为你没有在Class2中实现纯虚拟“方法”。
修改
好的我觉得我现在更好地理解你的问题,我认为错误在于Interface2。尝试将其更改为:
// specialized interface
class Interface2: public virtual Interface // ADDED VIRTUAL
{
public:
//virtual int method() = 0; COMMENTED THIS OUT
// some other methods here
};
Interface2不应该具有纯虚拟定义方法,因为它已经在接口中。
Interface的继承需要是虚拟的,否则当你从Class2中的Interface2和Base派生时,你会对Base ::方法产生歧义。
现在你应该发现它会编译,可能带有支配警告,当你调用c2.method()时,你得到27。
答案 3 :(得分:3)
关于Class
:您需要做的就是从Class
派生Base
- 隐含了它实现Interface
的事实,事实上,不可避免:
class Class: public Base // virtual inheritance is unnecessary here
{
};
Class
会根据需要从method()
继承Base
。
关于Class2
:
免责声明:未来的结果为负面
根据您对Tom's answer的评论,我认为我是Class2
的答案:
// concrete class - specialised
class Class2: public Interface2, public Base
{
public:
using Base::method; // "Imports" all members named "method" from Base
};
但实际上,这不起作用。通过C ++标准的挖掘揭示了这一点
第7.3.3节第14段解释了using
不能用于解决对继承成员的模糊访问:
... [注意:因为using-declaration指定了基类成员(而不是成员子对象或基类子对象的成员函数),所以using-declaration不能用于解析继承的成员歧义。 ...]
似乎在Class2
中获得所需行为的唯一方法是手动转发方法:
// concrete class - specialised
class Class2: public Interface2, public Base
{
public:
virtual int method() { return Base::method(); }
};
关于virtual
继承:您不需要Class
的声明,但您可能做需要Interface2
的声明以确保Class2
只有一个类型为Interface
的子对象 - 就目前而言,每个Class2
对象都有两个子对象:这个类型。 (虽然如果Interface
实际上是纯接口,但缺少成员变量,这不会导致问题。)如果有帮助,请绘制一个图表:每次基类出现时都没有关键字virtual
,作为一个独特的对象出现;使用关键字virtual
显示的所有基类都会压缩到一个对象中。
[更新:markh44's excellent answer表明上述方法(使Interface2
从Interface
虚拟地继承 )实际上允许{{1从Class2
自动继承method()
的实现!问题解决了!]
答案 4 :(得分:3)
如果您可能正在使用Interface或Base指针进行删除,还应该查看在接口中定义虚拟析构函数。
如果您执行以下操作,则无需虚拟析构函数,您将遇到问题:
Base *b = new Class2();
delete b;
答案 5 :(得分:2)
This answer在另一个论坛上似乎解决了你提到的确切问题。
答案 6 :(得分:1)
一般来说,你应该避免使用钻石入侵模式:
Interface
/ \
Base Interface2
\ /
Class2
如果你不小心,这将导致你在路上发出各种悲伤。歧义会咬你。
在您的特定实例中,Interface2不需要从Interface继承。 Interface2不需要指定“方法”,因为它是抽象的。删除Interface和Interface2之间的继承以打破菱形。然后你的层次结构看起来像:
Interface Interface Interface2
| | |
Base Base |
| \ /
Class Class2
您的实施如下:
// concrete class - not specialised - OK
class Class: public Base
{
};
// concrete class - specialised
class Class2: public Base, public Interface2
{
virtual int method() {return 35;}
virtual void Inteface2Method { ... }
};