继承的概念及定义
1.概念:继承是实现代码复用的重要手段,继承是类设计层次的复用
2.定义:
class Student :public Person
{
//...
}
Person是父类,也称作基类
Student是子类,也称作派生类
3.继承方式与访问限定符
->基类private成员在派生类中无论以什么方式继承都是不可见的(不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上,派生类对象在类中还是类外都不能访问它)
->class的默认继承方式是private,struct的默认继承方式是public
->protected限定符因派生而出现
->访问权限public>protected>private,基类的私有成员在子类中是不可见的,基类中的其他成员在子类中的访问方式==min(继承方式,成员在基类的访问方式)
->在实际运用中都是使用public继承。几乎很少使用protected/private继承
基类和派生类对象赋值转换
1.派生类对象可以赋值给基类的对象,基类的指针,基类的引用。(把派生类中基类的那一部分切割出去(切片,切割))
2.基类对象不能赋值给派生类对象
3.基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或引用(但是基类的指针指向派生类的对象才是安全的)
继承中的作用域
1.在继承体系中基类和派生类都有独立的作用域
2.子类和父类中若有同名成员,子类将屏蔽父类对同名成员的直接访问,这种情况叫做隐藏(或是重定义)(在子类成员函数中,可以使用 基类::基类成员 显示访问)
3.如果是成员函数的隐藏,只需要函数名相同就构成隐藏
4.在实际的继承体系中最好不要定义同名成员
派生类的默认成员函数
默认的意思是我们不写,编译器为我们自动生成
1.派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用
2.派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化
3.派生类的operator=必须调用基类的operator=完成基类的复制
4.派生类的析构函数会在调用完成之后,自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理,基类对象后清理的顺序
5.派生类对象初始化,先调用基类构造,再调用派生类构造
6.派生类对象析构清理,先调用派生类的析构,再调用基类的析构
7.编译器会对析构函数名进行特殊处理,处理成destructor(),所以基类析构函数不加virtual的情况下,基类析构函数和派生类析构函数构成隐藏关系
继承与友元
友元关系不能继承,即基类的友元不能访问派生类的私有和保护成员
继承与静态成员
基类定义了静态的static成员,则整个继承体系中只有一个这样的成员,无论派生出多少个子类,都只有一个static成员实例
复杂的菱形继承与菱形虚拟继承
单继承:一个子类只有一个直接父类时,称这个关系为单继承
多继承:一个子类有两个或以上直接父类,这个继承关系叫做多继承
菱形继承:菱形继承是多继承的一种特殊情况
菱形继承的问题:若A为基类,派生出B类和C类,B类和C类又派生出D类,那么D类的对象中会含有两份A类中的成员,这样就会产生数据冗余和二义性的问题
解决方法:使用虚拟继承可以解决此问题比如在B和C继承A时使用虚拟继承,但是虚拟继承不要在其他的地方使用
虚拟继承的原理:D对象中将A的成员变量放在了对象组成的最下面,这个A同时属于B和C,而B和C中的两个指针,指向两张表,指针叫做虚基表指针,表叫做虚基表,虚基表中存偏移量,通过偏移量可以找到D对象中下面的A
小结
类与类之间的关系
继承:public继承是一种is-a的关系,即每个派生类对象都是一个基类对象
组合:组合是一种has-a的关系,假如B组合了A,那么每一个B类对象中都有一个A类对象
优先使用组合而不是继承,因为组合的耦合度相对较低