我有一个基类'Base',这是一个纯虚拟类:
class Base {
public:
virtual void A() = 0;
virtual void B() = 0;
virtual ~Base() { } // Eclipse complains that a class with virtual members must have virtual destructor
};
我还有另外 2 个类,其中一个实现 A(),另一个实现 B():
class DerivedA : public virtual Base
{
public:
virtual void A() {
printf("Hello from A");
}
};
class DerivedB : public virtual Base
{
public:
virtual void B() {
printf("Hello from B");
}
};
声明中的 virtual 关键字应该解决菱形问题。
现在我想将这两个类合并到另一个类中,以便实现 A() 和 B(),如下所示:
class DerivedC: public DerivedA, public DerivedB {
// Now DerivedA and DerivedB are combined
};
// Somewhere else in the code
DerivedC c;
c.A();
c.B();
问题:尽管 G++ 可以很好地编译代码,但 Eclipse 给出了一个错误:The type 'DerivedC' must implement the inherited pure virtual method 'Base::B'
。使用Visual Studio进行编译时,我收到2个警告:
warning C4250: 'DerivedC' : inherits 'DerivedB::DerivedB::B' via dominance
warning C4250: 'DerivedC' : inherits 'DerivedA::DerivedA::A' via dominance
所以问题是:这样做的正确方法是什么?上面的代码是否会产生未定义的行为?
注意:标题可能有点误导,我不知道这个问题的标题是什么。
正确的方法是什么?上面的代码是否会产生未定义的行为?
该代码完全有效。这里没有未定义的行为。
通过 DerivedC
类对象对 A()
的非限定调用将始终调用 DerivedA::A()
,而通过 DerivedC
类对象对 B()
的非限定调用将始终调用DerivedB::B()
实例。
Visual C++ 会给你一个警告,因为你的代码使用了一个不太为人所知的虚拟继承功能,该功能对大多数普通用户来说可能并不明显,可能会让他们感到惊讶。在这种情况下,警告应被视为信息性的吹毛求疵,而不是警告。
请注意,C++ 标准不限制编译器对完全有效的代码发出信息性警告。警告 C4250 的文档提供了一个示例,告诉您视觉C++选择给出此警告的原因。
想试试这个:
class DerivedC: public DerivedA, public DerivedB {
public:
using DerivedA::A;
using DerivedB::B;
};
我自己无法用 Eclipse 或 VC++ 进行测试......
为什么编译器会抱怨这些;这是只是标准的混合技术。 类 Base
、 DerivedA
和 DerivedB
是抽象的,不能实例化,但这通常是混音的情况。 混合的全部意义在于它没有实现所有接口。 DerivedC
实现了这两种A()
并通过其继承成员B()
。
如果编译器拒绝编译此代码,则它被破坏。
至于警告...编译器可以自由地警告任何它喜欢的事情:
不要求具有虚拟成员的类具有虚拟析构函数。 在实践中,这通常是一个好主意,但是(除非析构函数受到保护),并且编译器警告是适当。
我猜,Visual Studio的警告是"信息丰富的",但是这个是语言被设计为工作的方式。 当然不是要避免的事情。 就此而言,我不认为这种优势实际上在这里起作用,因为
Base
中的函数是纯的虚拟。 Visual Studios似乎想说的是,在DerivedC
,A()
的实际过载是DerivedA::A()
,而不是Base::A()
. 这似乎是人们直觉上对我的期望;关于支配地位的规则实际上只是对人们会直观地期望什么。
无论如何,我肯定会关闭关于支配地位的警告。 有在这方面当然没有什么可担心的。 我会抱怨大声谈论一个没有编译代码的编译器。
Studio有一个编译器错误,在主导函数是纯虚拟的情况下发出警告C4250。 该错误已因"无法修复"而关闭;可接受的解决方案是使用以下方法禁止显示警告:
#pragma warning( disable: 4250 ) /* 'class1' : inherits 'class2::member' via dominance */
另请参阅 http://msdn.microsoft.com/en-us/library/6b3sy7ae(VS.80).aspx#CommunityContentHeader的讨论。
你的基类是抽象的:它不能被实例化。B 和 A 类也是抽象的,因为它们只实现一种方法。
这两种解决方案在文件DerivedC.cpp
void DerivedC::A(){
DerivedA::A();
}
void DerivedC::B(){
Derived:B();
}
或者你可以在头文件中使用using
关键字:
class DerivedC: public DerivedA, public DerivedB {
public:
using DerivedA::A;
using DerivedB::B;
};