文章目录
- 继承概念
- 切片和重定义
- 派生类的默认成员函数
- 菱形虚拟继承
继承概念
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用
基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。关键字class时默认的继承方式是private,使用struct时默认的继承方式是public
切片和重定义
派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。而基类对象不能赋值给派生类对象,因为子类继承父类后,可能会出现父类没有的成员变量,比如下面的 _No
在继承体系中基类和派生类都有独立的作用域,子类和父类中有同名成员,若不指明类域,局部优先,会先调用子类函数。子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义
派生类的默认成员函数
这六个默认成员函数主要遵守以下规则,先用初始化列表初始化基类的那一部分,在初始化子类自己这一部分。先析构派生类在析构父类,若先析构父类,而子类可能还调用了父类的内容,就会出现野指针等等问题。记住构造先父后子,析构先子后父就行
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例
菱形虚拟继承
菱形继承:菱形继承是多继承的一种特殊情况。菱形继承的问题:菱形继承有数据冗余和二义性的问题。在 Assistant 的对象中 Person 成员会有两份,从 Student 和 teacher 对象中各继承了一份,Assistant 对象若使用了 Person 成员,就会报错,指代不明确,除非指明是 Student 或者 teacher 的类域
菱形继承
class A
{
public:
int _a;
};
class B : public A
{
public:
int _b;
};
class C : public A
{
public:
int _c;
};
class D : public B, public C
{
public:
int _d;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 1;
d._b = 3;
d._c = 4;
d._d = 5;
return 0;
}
对于以上带代码,先看菱形继承的对象模型:因为 B,C 都有 _a, 如果 d._a 这样直接访问就会报错,不确定你访问的是B对象还是C对象中的_a
菱形虚拟继承
class A
{
public:
int _a;
};
class B : virtual public A
{
public:
int _b;
};
class C : virtual public A
{
public:
int _c;
};
class D : public B, public C
{
public:
int _d;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 2;
d._b = 3;
d._c = 4;
d._d = 5;
d._a = 0;
return 0;
}
对于以上带代码,先看菱形虚拟继承的对象模型:
偏移量表内容
这里可以分析出D对象中将A放到的了对象组成的最下面,这个A同时属于B和C。那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A
思考一:为什么要建偏移量表,而不直接存偏移量?
因为 A 类对象可能不止一个成员变量,如果多于一个的话,D 里面 B 和 C对象都要存多个偏移量,肯定没有直接放一个偏移量表地址合适,这样极大的减小了 D 对象的大小
思考二:为什么偏移量表里面不直接存 _a 的地址,而是存一个偏移量?
因为如果你表里存 _a 地址的话, D 实例化出的d1和d2 中的 _a 地址是不一样的,但它们相对B和C对象的偏移量却是相同的。因此存偏移量的话可以保证所有 D 实例化出的 d1, d2, d3等等对象,都公用同一张表