多继承是指一个派生类继承多个基类;多继承想要节省空间就需要用到虚继承,因此多继承或者说虚继承的一个弊端也显而易见,在菱形继承之下,B 继承A,C 继承A,D多继承B 和C,这样需要声明D 虚继承B和C ,还要在D的构造中指明A的构造方式。这样一来,D在看不到A的情况下,仍然需要指明A的继承方式。
虚继承是指为了节省空间而将相同的数据区域使用虚基类指针而代替的一种继承方式(在C++中而言),虚基类指针指向一个虚基类表,表里面记载着偏移,并且将虚基类中的数据放到派生类作用域下的最底层;Java中没有多继承,因此没有虚继承。
虚函数是指一个会发生动多态调用的类成员函数,虚函数会导致在类中生成一个虚函数指针,虚函数指针指向一张虚函数表,表里面记录着RTTI和偏移以及虚函数的入口地址;在这里提到一个C++中纯虚函数的概念,纯虚函数是指只有声明没有实现的普通类成员函数,表现为 virtual + 返回值 + 函数名(参数列表) = 0;拥有纯虚函数的类叫做抽象类。
若是没有指明类中的访问限定符,类中默认private,struct中默认public,若是缺省继承规则的话默认是private方式继承。
对以上概念进行简单的复习之后,我们将会面临画派生类对象的内存布局的问题;因此我们还需要以下画内存布局的一些规则;
1,非虚基类先排列
2,虚基类后排列
3,不是同一平行层次下的虚基类指针要向内层合并,平行层次在同一作用域下的虚基类指针也会合并;
4,在虚函数指针所在的作用域下,虚函数指针的优先级最高;
5,虚基类先构造,非虚基类后构造;
6,虚基类表合并后,第一行记录虚基类指针相对于当前作用域的偏移,以下几行分别记载虚基类指针相对于重复数据的偏移;
下面举个例子:
在面试中,我们还可能会被问到这样的问题,实现一个不能被继承的类?
不能被继承的类,我们首先考虑到派生类的生成需要调用基类的构造函数,如果基类的构造函数不能被调用,写在private访问限定符之下,那么就可以实现一个不能被继承的类了。但是那样一来,我们的基类自己也不能构造,所以我们可以用如下方式实现:
class A:
template<typename C>
class Base
{
private:
Base(){}
friend class C;
};
class A:virtual public Base<A>
{
public:
A(int a = 0):ma(a){}
private:
int mb;
}
这样一来我们的A类就是一个不能被继承的类,因为A类可以调用模板类(虽然模板类的构造函数写在了private下,但是生命里了A类是模板类的友元),但是由于友元关系不能继承,所以其他类继承A类的时候就会失败,因此现在的A类就是一个不能被继承但是却能被正常使用的类。