1.前言
继承? 继承老爸的财产。
目录
本节目标:
1.
继承的概念及定义
2.
基类和派生类对象赋值转换
3.
继承中的作用域
4.
派生类的默认成员函数
5.
继承与友元
6.继承与静态成员
2.继承的概念及定义
1.1继承的概念
继承 (inheritance) 机制是面向对象程序设计 使代码可以复用 的最重要的手段,它允许程序员在 保持原有类特性的基础上进行扩展 ,增加功能,这样产生新的类,称派生类。继承 呈现了面向对象程序设计的层次结构 ,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继 承是类设计层次的复用 。
如果你还是不明白 ,我举个例子你明白了。 世界上有一种高级生物叫做人类, 而人类又可以细分成三种 男人 女人 人妖 但是这三种人他们都有一些共同的属性 列如:年龄 身高 体重身份证号码 姓名 等等。但是他们又有不同属性 列如 职业 天赋 等等。这世界有太多的职业的每个职业我们定义出来 那就会有很多重复代码(年龄身高.....)这时我们就可以把共同属性抽象出来描述成一个类。请看下图
![](https://i-blog.csdnimg.cn/blog_migrate/4d87d678cf14db4f54d7127b051891b4.png)
学号只有学生才会有 这时候我们在继承 person这个类成员。
通过继承我们就得到了person类的保护成员。
下面我们看到 Person 是父类,也称作基类。 Student 是子类,也称作派生类。
1.2 继承定义
1.2.2继承关系和访问限定符
1.2.3继承基类成员访问方式的变化
总结:1. 基类 private 成员在派生类中无论以什么方式继承都是不可见的。这里的 不可见是指基类的私 有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面 都不能去访问它 。2. 基类 private 成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为 protected 。 可以看出保护成员限定符是因继承才出现的 。3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他 成员在子类的访问方式 == Min( 成员在基类的访问限定符,继承方式 ) , public > protected> private 。4. 使用关键字 class 时默认的继承方式是 private ,使用 struct 时默认的继承方式是 public , 不过最好显示的写出继承方式 。5. 在实际运用中一般使用都是 public 继承,几乎很少使用 protetced/private 继承 ,也不提倡使用 protetced/private 继承,因为 protetced/private 继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。
3.基类和派生类对象赋值转换
•派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用 。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。•基类对象不能赋值给派生类对象。•基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。
![](https://i-blog.csdnimg.cn/blog_migrate/1f60f0bf517a90ff7cd3a31c5f15807c.png)
3.继承中的作用域
那如果我非要访问父类的同名成员怎么办? 简单
结论:
1. 在继承体系中 基类 和 派生类 都有 独立的作用域 。2. 子类和父类中有同名成员, 子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。 (在子类成员函数中,可以 使用 基类 :: 基类成员 显示访问 )3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。4. 注意在实际中在 继承体系里 面最好 不要定义同名的成员 。
4.派生类的默认成员函数
请看下图
如果子类要初始化,要初始化父类的成员 不用person::(参数)直接 父名(参数)
同时也调用了析构函数 这里就不再过多解释构造函数和析构函数了,有疑问的可以去看之前的博客类和对象
拷贝构造也是一样
下面再看赋值重载 子类和子类赋值重载我就不说了,和前面3个默认函数一样的 主要讲讲子类赋值给父类
再看析构函数 根据前面3个函数来说 子类析构函数要加父类析构函数吗?
从结果来看 父类的析构函数多调用了一次 ,这样肯定有问题 如果是自定义类型有指针的情况话程序就崩溃了。
正确的做法看下图
子类的析构函数不用加 父类的析构函数 原因多态的时候再说。
总结:1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。3. 派生类的 operator= 必须要调用基类的 operator= 完成基类的复制。4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。5. 派生类对象初始化先调用基类构造再调派生类构造。6. 派生类对象析构清理先调用派生类析构再调基类的析构
5.继承与友元
先说结论:
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员
解决方法 在子类中 再次声明友元函数
6. 继承与静态成员
先说结论:
基类定义了 static 静态成员,则整个继承体系里面只有一个这样的成员 。无论派生出多少个子类,都只有一个 static 成员实例 。
7.复杂的菱形继承及菱形虚拟继承
这种为单继承
菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在 Assistant 的对象中 Person 成员会有两份。二义性好解决 类名加域限定符 就可以解决数据冗余如何解决:用虚函数解决, 在多态的时候再细讲 。关注我带你学习更多C++知识