【问题标题】:c++ why does virtual inheritance allow for the prevention of further inheritance?c++ 为什么虚拟继承允许防止进一步继承?
【发布时间】:2013-04-23 17:16:50
【问题描述】:

相关:Does "virtual base class in the case of multilevel inheritance" have significance

我有一个可以继承的模板类,以提供一些选择功能。但是,它希望阻止任何类进一步从继承它的任何东西继承。

以下似乎实现了这一点:

template<typename Child>
class SealingClass
    {
    public:
    /*public methods etc*/
    private:
    SealingClass() {}
    friend Child;
    };

//simplify a bit:
#define Seal( x ) public virtual SealingClass< x >

现在,我可以继承上面的类了,如下:

class NewClass: Seal(NewClass) {};

如果我再尝试从NewClass 继承,如:

class AnotherClass: public NewClass {};

然后创建一个该类的实例:

AnotherClass a;

我得到了想要的错误,关于 SealingClass 中的构造函数是私有的。

所以,一切都如我所愿!

但是,我注意到如果我从定义中删除 virtual 关键字..

#define Seal( x ) public SealingClass< x >

..我的 AnotherClass 实例化现在可以正常工作了。

我了解virtual 关键字在这种情况下意味着在可能存在多个实例的多重继承(例如菱形继承)的情况下只定义基类的一个实例,从而导致模棱两可的函数调用等等

但是,为什么会影响上面的功能呢?

谢谢:)

【问题讨论】:

  • 通过仔细阅读上面链接的答案,这听起来像是因为虚拟继承的类被实例化的顺序,这是有道理的!
  • IIRC 您必须在最派生类型中初始化一个虚拟基类,而在下一个派生类型中初始化非虚拟基类。
  • 我假设你也有一个private 复制构造函数?否则,您可以简单地让所有子构造函数使用它来构造 SealingClass,完全绕过密封。

标签: c++ inheritance virtual


【解决方案1】:

如果使用虚拟继承,最派生的类型必须做这个虚拟基类的初始化。如果不使用虚拟继承,则直接派生的类型必须进行初始化。

因此,私有ctor不会阻止派生类型NewClass初始化直接基类SealingClass,如果AnotherClass没有被虚拟继承,则不必初始化NewClass


一些例子:

template<typename Child>
class SealingClass {
public: // for now
    SealingClass() {}
};

class NewClass : public SealingClass<T> {
public:
    NewClass() : SealingClass<T>() {} // allowed, SealingClass<T> is a
                                      //   direct base class
};

class AnotherClass : public NewClass {
public:
    AnotherClass() : NewClass() {}        // allowed, NewClass is a
                                          //   direct base class
    AnotherClass() : SealingClass<T>() {} // not allowed, SealingClass<T> is
                                          //   no direct nor a virtual base class
};


class NewClass_v : public virtual SealingClass<T> {
public:
    NewClass_v() : SealingClass<T>() {}   // allowed, SealingClass<T> is a
                                          //   direct base class
};

class AnotherClass_v : public NewClass_v {
public:
    AnotherClass_v() : NewClass_v() {}        // allowed, NewClass_virt is a
                                              //   direct base class
    AnotherClass_v() : SealingClass<T>() {}   // allowed, SealingClass<T> is a 
                                              //   virtual base class
};

现在,如果 SealingClass 的 ctor 是私有的,则由于 private 访问说明符而不是朋友,AnotherClass_virt 不允许调用此 ctor。

如果你省略基类的显式初始化(​​无论是虚拟的还是直接的),它是默认初始化的([class.base.init]/8),也就是说,默认的ctor被隐式调用(但是你仍然必须可以访问 ctor,因此它与显式写入对默认 ctor 的调用相同。


一些引用:

[class.base.init]/1

在类的构造函数的定义中,直接和虚拟基础子对象的初始化器和非静态数据成员可以由 ctor-initializer 指定

[class.base.init]/7

mem-initializer-id 表示虚拟基类的 mem-initializer 在执行不是最派生类的任何类的构造函数期间被忽略。

[class.base.init]/10

在非委托构造函数中,初始化按以下顺序进行:

  • 首先,并且仅对于最派生类的构造函数,虚拟基类按照它们在基类的有向无环图的深度优先从左到右遍历中出现的顺序进行初始化,其中“从左到右”是派生类基说明符列表中基类的出现顺序。
  • 然后,直接基类将按照声明顺序初始化,因为它们出现在 base-specifier-list 中(无论 mem-initializer 的顺序如何)。

强调我的。

【讨论】:

  • 谢谢;最后引用 First, and only for the constructor of the most derived class, virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes.. 对于查看这里发生的事情特别有用!
猜你喜欢
  • 2017-03-31
  • 2016-03-26
  • 2011-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多