钻石继承方案在 G++ 中编译良好,但在 VC++/Eclipse 中产生警告/错误



我有一个基类'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++ 进行测试......

我不知道

为什么编译器会抱怨这些;这是只是标准的混合技术。 类 BaseDerivedADerivedB是抽象的,不能实例化,但这通常是混音的情况。 混合的全部意义在于它没有实现所有接口。 DerivedC实现了这两种A()并通过其继承成员B()

如果编译器拒绝编译此代码,则它被破坏。

至于警告...编译器可以自由地警告任何它喜欢的事情:

  • 不要求具有虚拟成员的类具有虚拟析构函数。 在实践中,这通常是一个好主意,但是(除非析构函数受到保护),并且编译器警告是适当。

  • 我猜,Visual Studio的警告是"信息丰富的",但是这个是语言被设计为工作的方式。 当然不是要避免的事情。 就此而言,我不认为这种优势实际上在这里起作用,因为Base中的函数是纯的虚拟。 Visual Studios似乎想说的是,在 DerivedCA()的实际过载是DerivedA::A(),而不是 Base::A() . 这似乎是人们直觉上对我的期望;关于支配地位的规则实际上只是对人们会直观地期望什么。

无论如何,我肯定会关闭关于支配地位的警告。 有在这方面当然没有什么可担心的。 我会抱怨大声谈论一个没有编译代码的编译器。

已知Visual

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;
};

最新更新