← C++ 高级程序设计(2020) 2020-C++高级程序设计-C++ 继承 2020-07-01 约 13 分钟阅读 阅读 — 次
继承
1. 继承机制
基于目标代码的复用
对事物进行分类
派生类是基类的具体化
把事务(概念)以层次结构表示出来,有利于描述和解决问题
增量开发(面向接口编程)
2. 单继承
protected:
如果没有继承的话,protected和private是相同的
派生类可以访问基类中protected的属性的成员。
派生类不可以访问基类中的对象 的protected的属性。
派生类含有基类的所有成员变量
2.1. 核心代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Student { int id; public : char nickname[16 ]; void set_ID (int x) {id = x;} void SetNickName (char *s) {strcpy (nickname,s);} void showInfo () {cout << nickname << ":" << id << endl ;} void showInfo (int x) {cout << x << endl;} };class Undergraduated_Student : public Student { int dept_no; public : void setDeptNo (int x) {dept_no = x;} void showInfo () {cout << dept_no << ":" << nickname << endl;} void set_ID (int x) {……} void showInfo () { cout << dept_no << ":" << nickname << endl; } private : Student::nickname; void SetNickName () ; }; Undergraduated_Student us; us.showInfo (10 );
2.2. 继承方式
public、private:访问权限只和基类中的访问权限有关
2.2.1. public
public:class Undergraduated_Student: public Student
原来的public是public,原来的private是private
如果没有特殊需要建议使用public
2.2.2. private
private:原来所有的都是private,但是这个private是对于Undergraduate_Student大对象而言,所以他自己还是可以访问的。
默认的继承方式
2.2.3. protected
如果没有继承的话,protected和private是相同的
派生类可以访问基类中protected的属性的成员。
派生类不可以访问基类中的对象 的protected的属性。
派生类含有基类的所有成员变量
2.3. 继承声明方式
1 2 3 4 class Undergraduated_Student : public Student;class Undergraduated_Student ;
2.4. 基类和继承类的方法关系
2.4.1. 派生类中的showInfo()
派生类中的showInfo():Overwirtten 重写(绝对不是覆盖) ,隐藏基类的showInfo()函数
而并不是覆盖操作
2.4.2. 基类的showInfo()
如果基类中有一个void ShowInfo(int x):那么是不是从基类可以进行调用呢?
不可以(所有函数都被隐藏)
因为重定义将名空间进行了覆盖
父类中的所有的函数都不可见:但是我们可以通过指定名空间来完成访问:using Student::showInfo,所有的版本都可以见,这时候是重写。
2.4.3. 方法调用的顺序
首先在名空间中按照名称进行匹配
一旦名称匹配,则会校验函数参数
匹配不上是不会去别的名空间进行匹配(也就是不会去student那里去匹配)
2.5. 方法覆盖
我们需要指明覆盖:virtual:在对应想要重写的函数的前面写上一个virtual
虚函数实现的是多态
2.6. 不可以被继承的部分
构造函数和析构函数是不可以被继承的:是对类进行初始化的,无法继承
运算符重载函数也是不可以被继承的
2.7. 访问权限的修改方法
1 2 3 private : Student::nickname; void SetNickName () ;
声明char nickname[16];并没有修改可变性,语法无误,语义不对;
3. 继承的初始化
派生类对象的初始化
构造函数的执行次序
基类的构造函数
派生类对象成员类的构造函数(注意!)
派生类的构造函数
析构函数的执行次序(与构造函数执行顺序相反)
派生类的析构函数
派生类对象成员类的析构函数
基类的析构函数
基类构造函数的调用
缺省执行基类默认构造函数
如果要执行基类的非默认构造函数 ,则必须在派生类构造函数的成员初始化表中指出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class A { int x; public : A () {x = 0 ; } A (int i) { x = i; } };class B : public A { int y; public : B (){y = 0 ;} B (int i) {y = i;} B (int i, int j):A (i){ y = j; } B (const B& b){ } }; B b1;B b2 (1 ) ;B b3 (0 ,1 ) ;class B : public A{ public : using A::A; }
4. 多继承
4.1. 定义
定义
1 2 3 class <派生类名 > :[<继承方式 > ] <基类名1 > , [<继承方式 > ] <基类名2 > ,… {〈成员表〉}
Java不允许多继承,是因为多继承非常复杂。
继承方式:默认是private的继承方式:public、private 、protected
继承方式及访问控制的规定同单继承:重复进行继承
派生类拥有所有基类的所有成员
可以睡的沙发:继承sofa和Bed
setWeight重名:两个基类有相同部分,我们会拆分基类
之后我们拆分出来setWeigth()(Base Class Decomposition)
形成菱形结构:还有问题,Weigth变量依旧在,已然有两个Weigth部分
解决方案:虚继承
4.2. 基类声明顺序(初始化顺序)
基类的声明次序决定:
对基类构造函数/析构函数的调用次序(顶部基类,同层基类按照声明顺序) 上图中就是 ABCD的顺序
对基类数据成员的存储安排
析构函数正好相反
4.2.1. 名冲突
<基类名>::<基类成员名>
成交变量和成员函数的重名问题:在上图中,D中会有B和C的x
问题:每次setWeight到底是设置谁的?
4.2.2. 虚基类
如果直接基类有公共的基类,则该公共基类中的成员变量在多继承的派生类中有多 个副本
如果有一个公共的虚基类,则成员变量只有一 个副本
类D有两个x成员,B::x,C::x
虚继承:保留一个虚指针
虚指针指向A
可以认为是一个组合关系
合并
1 2 3 4 5 class A ;class B : virtual public A;class C : public virtual A;class D : B, C;
4.2.3. 虚基类注意
虚基类的构造函数由最新派生出 的类的构造函数调用
原来是B构造一份A,C构造一个A
而现在是由D调用A的构造函数,在D的时候先调用A的构造函数,在B和C的时候不在调用A的构造函数,而只是存放指针
虚基类的构造函数优先非虚基类的构造函数执行
如果有两个基类,两个类有一个相同名称的虚函数,比如B和C都有一个同名的虚函数,到底怎么做?不做要求
4.2.4. 多继承理解
通过VS的c++命令行中选择打印一些参数
Ex:/d1 reportAllClassLayout,可以看到所有类的布局如下
构造D,将B的v3覆盖
之后在第一个A上面直接添加一个虚函数表中的记录
可以分开覆盖,也可以各自覆盖
评论