14

我有一个基类“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

所以问题是:这样做的正确方法是什么?上面的代码会产生未定义的行为吗?

注意:标题可能有点误导,我不知道这个问题的好标题是什么。

4

5 回答 5

8

这样做的正确方法是什么?上面的代码会产生未定义的行为吗?

该代码完全有效。这里没有未定义的行为。通过类对象
的非限定调用将始终调用,而通过类对象的非限定调用将始终调用实例。A()DerivedCDerivedA::A()B()DerivedCDerivedB::B()

Visual C++ 给你一个警告,因为你的代码使用了一个不太为人所知的虚拟继承特性,这对大多数普通用户来说可能并不明显,并且可能会让他们感到惊讶。在这种情况下,应将警告视为信息性吹毛求疵,而不是警告。

请注意,C++ 标准不限制编译器针对完全有效的代码发出信息警告。警告 C4250的文档提供了一个示例,告诉您 Visual C++ 选择发出此警告的原因。

于 2012-08-15T08:09:55.330 回答
6

你可能想试试这个:

class DerivedC: public DerivedA, public DerivedB {
public:
    using DerivedA::A;
    using DerivedB::B;
};

我自己无法使用 Eclipse 或 VC++ 进行测试...

于 2012-08-15T07:40:42.803 回答
4

我不知道为什么编译器会抱怨这些;这只是标准的混合技术。类Base,DerivedADerivedB是抽象的,不能被实例化,但是对于 mixins 来说通常是这样的。mixin 的全部意义在于它没有实现所有的接口。并通过 其继承的成员DerivedC实现。A()B()

如果编译器拒绝编译这段代码,它就会被破坏。

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

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

  • 我猜来自 Visual Studio 的警告是“信息丰富的”,但这是该语言设计的工作方式。这当然不是要避免的事情。就此而言,我认为支配地位实际上并没有在这里发挥作用,因为其中的功能Base是纯虚拟的。Visual Studios 似乎想说的是,在 中 , isDerivedC的实际重载,而不是 。这似乎是人们对我的直觉期望;关于支配地位的规则实际上只是对人们直觉期望的正式陈述。A()DerivedA::A()Base::A()

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

于 2012-08-15T08:20:32.130 回答
2

众所周知,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上的讨论。

于 2012-08-15T08:31:42.563 回答
0

您的 Base 类是抽象的:它不能被实例化。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;
};
于 2012-08-15T07:44:26.130 回答