这个问题的内容取决于我对虚拟继承在C ++中如何工作的理解。如果有任何错误,请纠正我。
在C ++中,虚拟继承需要(隐式或显式)调用子类中所有祖先类的构造函数,这也会阻止任何祖先调用另一个祖先的构造函数。换句话说:
class Grandparent{
public:
Grandparent(string const& message) {
std::cerr << "Grandparent says " << message << '\n';
}
};
class Parent : virtual Grandparent{
public:
Parent(string const& message)
: Grandparent(string("hello parent. Parent says ") + message)
{}
}
class Child : virtual Parent{
public:
Child(void)
: Grandparent(""), // Required, since Grandparent has no default ctor
Parent("hello child. Child says are we there yet"?)
{}
}
在此示例中,构造Child
的实例应该只打印出“祖父母对标准错误说”,因为Parent
构造函数对Grandparent
构造函数的调用被删除了。换句话说,由子进程执行的“有效”Parent
构造函数如下所示:
Parent(string const& message) {}
但这似乎违反直觉并且存在问题。当父类已经这样做时,为什么子类必须显式调用祖父母的构造函数?我知道diamond problem,它不适用于此处,因为子类只有一个直接父级。该语言保证父母已经调用其祖父母的构造函数,那么为什么要将这个任务委托给孩子?
此外,父母对祖父母构造函数的调用的擦除阻碍了继承的有效性。如果孩子想要真正继承父母的行为,孩子必须复制一些父母的初始化代码。例如:
Parent::Parent(string const& msg)
: Grandparent(some_parent_logic(msg))
{}
Child::Child(string const& msg)
: Parent("doesn't matter anymore"),
GrandParent(some_child_logic(some_parent_logic(msg))) // Can't inherit implicit use of parent logic
如果在调用祖先构造函数时不使用复杂的逻辑,这不是一个大问题,但随着继承链变长,冗余问题会随之增加。而且,从OO的角度来看,当孩子只是直接从其中一个祖先继承时,需要手动处理所有祖先,这似乎有点亵渎神明。
我想我最后要问的是:
Child::Child(void): Parent1(), Parent2() {}
导致Parent1
初始化Grandparent
,而Child::Child(void): Parent2(), Parent1() {}
导致Parent2
初始化Grandparent
,而Child::Child(void): Grandparent("The child overrides."), Parent1(), Parent2()
显式初始化Grandparent
}}。答案 0 :(得分:0)
也许语言设计师本可以做得更好(老实说,我只发现了几个虚拟继承在15年的c ++开发中有意义的情况),但是我们的规则只是要求最派生的类构造实际上是继承的基础。它确实有一定道理,因为只有在构建了最衍生的类型时,我们才能真正知道虚拟基地将“活着”的位置。
除了派生最多的类型构建虚拟基础的任何规则都会使已经复杂的业务变得更加复杂。