C++ 虚继承和虚继承
C++ 虚继承和虚继承
虚继承是在多继承中为了解决冲突而技术。学术一点来说,是指一个指定的基类,在继承体系结构中,将其成员数据实例共享给也从这个基类直接或间接派生的其他类。
虚继承非常有用,可以避免多继承的歧义和多重拷贝。
为什么需要虚继承?
考虑有如下继承结构。
B和C继承A,D多继承B、C,我们看以下代码。
class A {
public:
virtual void sayHi();
};
class B :public A {
public:
virtual void onlyB();
};
class C :public A {
public:
virtual void onlyC();
};
class D :public B, public C {
};
到这里,可能聪明的读者已经看出来有什么问题了。如果我们对D进行实例化。
D d;
A& a = d;
这么写的话,编译器会无情的报错。例如VS2019就会说这是一种不明确的转换。没错,因为按照上面的定义,D是一个有歧义的类。因为D间接地继承了两次A(B继承了,C继承了,D又继承了B和C),所以所有的D对象就会有两个不同的由A派生的派生对象(B和C)。因此,尝试直接对其进行引用,编译器会一头雾水,这个引用到底是想指向哪个A啊?究竟是 B::A 还是 C::A ?所以这样是不对的。
当然,我们可以使用static_cast进行强制类型转换,但是这不是我们今天学习的目标。
我们可以看到,我们不需要两个A,不需要A被继承两次。我们只是需要一个层次结构来说明A、B、C、D之间的关系。假如A是职业,B是销售,C是经理,我们仅仅想表示D是一个销售经理。而不意味着D是两个A,销售经理是一种职业而不是两种职业。而且从上面这个特殊的例子来看,B和C并没有重载sayHi()这个函数,sayHi()继承到D这里仍是之前那套运作规律,所以我们也不需要两份一模一样的sayHi()。
于是,虚继承便横空出世来解决这个问题了。
虚继承
我们如果这么声明继承,
class A {
public:
virtual void sayHi();
};
// virtual
class B :public virtual A {
public:
virtual void onlyB();
};
class C :virtual public A {
public:
virtual void onlyC();
};
class D :public B, public C {
};
如果在继承的类之前加上一个virtual关键词,代表该继承是虚继承。这样 B::A 和 C::A 现在是一个A了。这样,D中有且仅有一个共享的A。相当于间接派生类D直接穿透它的基类B和C,直接继承了A。这样,如果再创建A对D的引用,也不会有歧义了。因为只存在一种可以转换路径,那就是直接由A到D。
这个A就是所说的虚基类。由于虚基类是多个派生类共享的基类,因此必须明确到底谁来初始化这个虚基类。C++标准规定,由最后派生的类来初始化虚基类。因此,对于间接继承了虚继类的类,也必须能直接访问虚继承来的祖先类,能够访问其虚函数表。
例如,之前的例子当中,B和C、D的构造函数初始化列表中都可以给出虚基类的初始化,但是只能由D的构造函数执行虚基类的初始化。