8

为什么建议在虚拟基类中不要有数据成员?

功能成员呢?如果我有一个所有派生类共有的任务,虚拟基类是否可以执行该任务,或者派生类是否应该从两个类继承 - 从虚拟接口和执行任务的普通基类?

谢谢。

4

5 回答 5

11

作为一种实践,您应该只使用虚拟继承来定义接口,因为它们通常与多重继承一起使用,以确保派生类中只存在一个版本的类。纯接口是最安全的多重继承形式。当然,如果你知道你在做什么,你可以随意使用多重继承,但如果你不小心,它可能会导致脆弱的代码。

虚拟继承的最大缺点是它们的构造函数是否带参数。如果必须将参数传递给虚拟基类的构造函数,则强制所有派生类显式调用构造函数(它们不能依赖调用构造函数的基类)。

我能看到您明确建议的唯一原因是您的虚拟基类中的数据可能需要构造函数参数。

编辑 马丁发表评论后,我做了一些家庭作业,谢谢马林。第一行并不完全正确:

作为一种实践,您应该只使用虚拟继承来定义接口,因为它们通常与多重继承一起使用,以确保派生类中只存在一个版本的类。

如果基类是纯接口,则虚拟继承没有区别(如果没有实现所有方法,则在 vc8 中编译器错误略有不同)。如果基类有数据,它只会产生真正的区别,在这种情况下,你最终会得到一个菱形而不是 U 形

Non virtual    virtual
  A     A          A
  |     |        /   \
  B     C       B     C
   \   /         \   /
     D             D

在虚拟情况下,B 和 C 共享 A 的相同副本。

然而,我仍然同意纯接口是最安全的多重继承形式,即使它们不需要虚拟继承。而且构造函数参数和虚拟继承是一个痛苦的事实。

于 2009-07-28T11:31:18.787 回答
2

核心建议是在虚拟基础中有一个默认构造函数。如果你不这样做,那么每个派生最多的类(即任何子类)都必须显式调用虚拟基 ctor,这会导致愤怒的同事敲你办公室的门......

class VirtualBase {
public:
    explicit VirtualBase( int i ) : m_i( i ) {}
    virtual ~VirtualBase() {}

private:
    int m_i;
};

class Derived : public virtual VirtualBase {
public:
    Derived() : VirtualBase( 0 ) {} // ok, this is to be expected
};

class DerivedDerived : public Derived { // no VirtualBase visible
public:
    DerivedDerived() : Derived() {} // ok? no: error: need to explicitly
                                    // call VirtualBase::VirtualBase!!
    DerivedDerived() : VirtualBase( 0 ), Derived() {} // ok
};
于 2009-07-28T11:33:18.033 回答
1

我从来没有见过这个推荐。

类是一组密切相关的功能和数据。基类的目的是拥有一组可供派生类重用的通用函数和数据。

我认为限制自己在基类中没有数据成员或非纯虚函数会减少代码重用量,从长远来看会导致代码可靠性降低。

于 2009-07-28T11:31:38.193 回答
0

a) 成员应该是私有的 - 所以在派生类中使用它们可能会遇到问题(所以你必须添加 getter 和 setter 方法,这会破坏你的界面)

b)不要声明您当前不使用的东西 - 仅在访问/使用它们的类中声明变量

c) 虚拟基类应该只包含接口/虚拟方法,仅此而已

我希望这会有所帮助,即使我的理由并不完美和完整:)

乔,克里斯

于 2009-07-28T10:07:26.210 回答
0

用于在 C++ 中模拟接口的全抽象基类不应该有数据成员——因为它们描述的是一个接口,而实例状态是一个实现细节。

除此之外,包含虚函数的基类很可能有数据成员。但是,通常的规则适用:

  • 使它们尽可能隐藏,如果您需要保留类不变量,请使用 getter 和 setter。
    (这里有一个漏洞:如果没有与成员关联的不变量,即它可以在任何给定时间假定任何可能的值,您可以将其公开。但是,这会拒绝派生类添加不变量。
  • 继承设计:你的类的契约应该定义派生类的责任和可能性,它需要/可以覆盖什么目的。
于 2009-07-28T12:05:20.107 回答