继承和派生可以认为是同一问题,从不同角度看得到的两个定义:
为保持已有类的特性,基于已有类构成新类的过程称为继承;
在已有类的基础上,添加自己的特性而产生的新类称为派生;
( 其中,被继承的类称为父类,派生出的新类被称为子类 )
继承的目的: 实现设计与代码的重用,充分利用原有的类
派生的目的: 当新的问题出现,原有程序无法解决,需要对原有程序进行改造。
派生类的定义:
class child : 继承方式 基类名{ 新成员属性 } // 多继承时派生类的定义 class child : 继承方式 基类名,继承方式 基类名,继承方式 基类名{ 新成员属性 }三种继承方式: 私有继承、保护继承、公有继承 说明:基类所有特性,派生类都能继承,但是对于基类私有属性,派生类无论如何也不能访问(被编译器隐藏);对于其他类型的属性,取 “ 继承 ” 与 “ 属性 ” 访问权限最严格的方式执行
类的继承后方法属性变化:
private 属性不能够被继承使用private继承,父类的protected和public属性在子类中变为private使用protected继承,父类的protected和public属性在子类中变为protected使用public继承,父类中的protected和public属性不发生改变补充说明:private, public, protected 三类访问标号的访问范围:
private 属性:1.该类中的函数;2.其友元函数访问;该类的对象不能访问protected 属性:1.该类中的函数;2.子类的函数;3.其友元函数访问;该类的对象不能访问public属性:1.该类中的函数;2.子类的函数;3.其友元函数访问;4.该类的对象可以访问对于成员变量:
当子类成员和父类成员同名时,子类依然从父类继承同名成员。子类成员和父类成员同名,子类默认访问子类的成员。在子类中,可以通过作用域运算符进行同名成员区分(base::para)在类外可以通过在基类中定义相关接口函数获取基类同名成员对于成员函数:
对于同名成员函数,其实指在派生类中重新定义基类的重载函数,学习了这篇博文,更加清晰了一些:原文链接
说明:
派生类可以继承基类中的非私有函数成员,当然也就可以继承其中非私有的被重载的函数。若要在派生类中重写其中的一个重载函数,则基类中的该函数以及该函数的重载函数在派生类的定义和实现中都会被屏蔽掉,对于派生类内,可以通过作用域运算符再次调用被重载的对象,类外也不可以直接调用源重载版本,但不影响派生类重写的函数的调用。如果派生类想通过自身类型使用基类中重载版本,则派生类必须要么重定义所有重载版本,要么一个也不重定义。 ==》问题就来了:如果在派生类中需要且仅需要重写其中一个重载函数,该如何操作? 方案有二:1、通过using在派生类中为父类函数成员提供声明 即添加 “ using Base::print; ” 2、通过基类指针调用(进阶办法:把在派生类中需要重载的那个版本相应地在基类中声明为vitual)多继承常会产生二义性问题,尤其针对两个基类中有同名的函数或者变量时(菱形继承),解决方案:显示地指定调用那个基类的版本,此举虽然能解决二义性问题,但重复继承问题并未解决。终极解决方式——虚继承方式
对于菱形继承中最顶层与直接继承一致时,但第二层开始,菱形继承若直接继承基类,则各自继承基类的一份属性;而虚继承方案则不仅继承了基类属性,还生成了一个虚指针,指向虚地址表,此时派生类的大小不仅包括基类属性,还包括一个地址指针;到菱形继承的第三层,对于传统继承,会产生二义性问题,但此时一般编译器会进行一定的优化,而对于虚继承方式,则派生类不仅继承基类属性,还继承第二层的两个派生类的虚指针,并调整了虚指针与虚基类首地址的偏移量。
虚继承可以解决具备公共祖先的多继承问题,对于没有公共祖先的多继承不能解决
关于虚继承的说明:当使用虚继承时,虚基类是共享的,无论被继承多少次,对象内存中只有一个虚基类子对象。其初始化也只能由一个类初始化一次;C++标准中要求每次继承之类中均需要写初始化语句,但虚基类的初始化是由最后的子类完成,其他初始化子类均不会调用。
前提是基类构造函数为“含参构造函数”,每一次继承,子类中才必须书写初始化语句为避免重复初始化,虚基类的初始化是由最后的子类完成,其他的初始化语句都不会调用