相关: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
中的构造函数是私有的。
所以,一切都按照我的意愿行事!
但是,我注意到如果我从define ..
中删除virtual
关键字
#define Seal( x ) public SealingClass< x >
..我AnotherClass
的实例化现在运行得很好。
我理解在这种情况下,virtual
关键字意味着在多重继承(例如钻石继承)的情况下只定义了基类的一个实例,其中可能存在多个实例,导致模糊不清函数调用等。
但是,为什么它会影响上述功能?
谢谢:)
答案 0 :(得分:7)
如果使用虚拟继承,则派生程度最高的类型必须初始化此虚拟基类。如果不使用虚拟继承,则直接派生类型必须进行初始化。
因此,私有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是私有的,则由于AnotherClass_virt
访问说明符而不是朋友,private
不允许调用此ctor。
如果省略基类的显式初始化(无论是虚拟还是直接),它都是默认初始化的([class.base.init] / 8),也就是默认调用默认的ctor(但是你仍然必须有权访问ctor,所以它与显式写入默认ctor的调用相同。)
一些引言:
[class.base.init] / 1
在类的构造函数定义中,直接和虚拟基础子对象的初始化程序和非静态数据成员可以由ctor-initializer指定
[class.base.init] / 7
mem-initializer,其中mem-initializer-id表示虚拟基类,在执行任何不是派生类最多的类的构造函数时会被忽略。
[class.base.init] / 10
在非委托构造函数中,初始化按以下顺序进行:
- 首先,仅对于派生程度最高的类的构造函数,虚拟基类按照它们在有向无环图的深度优先从左到右遍历中出现的顺序进行初始化。基类,其中“从左到右”是派生类base-specifier-list中基类出现的顺序。
- 然后,直接基类按声明顺序初始化,因为它们出现在base-specifier-list中(无论mem-initializers的顺序如何)。
强调我的。