当前课程知识点:C++语言程序设计进阶 > 第八章 多态性 > override与final > override与final
多态行为的基础:基类声明虚函数,继承类声明一个函数覆盖该虚函数
覆盖要求: 函数签名(signatture)完全一致
函数签名包括:函数名 参数列表 const
下列程序就仅仅因为疏忽漏写了const,导致多态行为没有如期进行
- C++11 引入显式函数覆盖,在编译期而非运行期捕获此类错误。 - 在虚函数显式重载中运用,编译器会检查基类是否存在一虚拟函数,与派生类中带有声明override的虚拟函数,有相同的函数签名(signature);若不存在,则会回报错误。
C++11提供的final,用来避免类被继承,或是基类的函数被改写 例: struct Base1 final { };
struct Derived1 : Base1 { }; // 编译错误:Base1为final,不允许被继承
struct Base2 { virtual void f() final; };
struct Derived2 : Base2 { void f(); // 编译错误:Base2::f 为final,不允许被覆盖 };
大家好
欢迎回来继续学习
C++语言程序设计
这一节我们来学习
C++11标准提供的
override和final的新功能
我们知道多态行为的基础
是什么呢
是基类声明一个虚函数
然后呢
派生类要去覆盖这个虚函数的话
就要声明一个
原型完全一样的函数
但是有的时候呢
我们写程序可能会有疏漏
本意可能想写一个虚函数
去覆盖基类的虚函数
但是写的时候呢
可能参数表
或者是返回值
或者什么地方没有把它
定义的跟基类虚函数的原型
完全一致
这个时候
编译器并不会报错
因为编译器
并不知道我们的意图是说
这个函数
是一个覆盖基类虚函数的函数
编译器没办法知道我们这个意图
那所以呢
有的时候我们在派生类中
这个函数原型没有写对的话
就没有达到相应的目的
就实现不了多态了
而这个错误呢
却在运行中反映出来
还比较难调试和定位
这是一个因为疏忽造成了错误
于是没有能够实现多态性的例子
我们看在Base类里面
定义了一个虚函数f1
它是一个const
是一个常成员函数
那么在Derived类里面
我们本来是希望覆盖
这个虚函数f1的
所以定义了一个
同名的参数表也一样
返回值也一样的函数
在这里我们希望覆盖
基类继承过来的虚函数
那这个地方呢
它没有写这个vitual这个关键字
当然就是认为
这个语法规定
只要继承过来这个函数了
那派生类中的同原型的函数
自动都是虚函数了
不用写这个vitual关键字了
确实它是可以缺省的
但是就是疏忽了一点
const也是区别不同
重载函数的一个因素
所以这个有const
和没有const这两个函数原型
它是不同的
所以没有达到
写一个新的虚函数去覆盖
原有继承的虚函数的这样的效果
所以在主函数中
我们用基类的指针
去指向基类的对象
去指向派生类的对象
然后调用f1
结果呢
调用的都是基类的这个f1
没有实现我们希望的多态性
那么这样的错误
是很难查找的
在运行时发现结果
跟我们预期的不一样
但是要找到原因
还是有点麻烦的
那么怎么避免这种情况呢
后面要给大家介绍
C++11引入了一种
显示的函数覆盖的功能
这样的话
编译器在编译期间就会发现
你的本意是想覆盖基类的虚函数
但是你没有写对啊
它就能把这种错误发现
给我们指出来
那么这里呢
就要用到这个override
我们用override说明的函数
就必须在基类中找到
同样原型的这个虚函数
如果你在派生类中
写了一个函数
用override说明了
但是编译器在它的基类中
找不到
跟它相同原型的一个虚函数
编译器就会报错
说这个错了
你没有找到相同原型的这个函数
你就会发现
我想写一个函数去覆盖
结果没写对
那么这样我们就能把错误
在编译阶段就修改了
那这样就避免了运行时
那种不稳定性
运行时发生的错误
其实是挺难调试的
我们定义类的时候
是不是有的时候觉得这个类
它的功能都很关键
我定义了这样一个类
其他人就用就好了
我不希望它被继承和发展
实际上就是不希望它被扩展
这个继承呢
跟我们人类说的那个继承遗产呢
还是有差别的
只要是好东西
我们不会说不希望继承的
但是程序中的继承呢
意味着你一定要对它进行修改的
为什么呢
因为不需要修改
你可以直接使用
拿来用就行了
不用经过一个继承的环节
所以程序中的继承
和我们日常所理解的继承呢
还是些许差别的
所以有的时候呢
我们定义一个类
不希望它被继承
也就是不希望通过这个类
去派生出新类来
想用你就用
但是你不许改它
为什么呢
有时候有的类里面的功能
非常关键
你希望在整个系统中
就这样使用这样统一的功能
不希望它被
功能被覆盖 被修改
不希望接口被屏蔽 对吧
还有的时候呢
并不是一个类里面的所有功能
都如此的关键
可能就是个别的成员函数
它的算法你希望固定
希望在整个系统里面
都用严格的用这同样的算法
而不要去修改
这个时候呢
你可以说这个类可以被继承
但是这个函数不希望被修改
那么这个时候呢
你可以在函数上使用final
去说明
那这个函数就不能被覆盖了
那么现在
我们通过一个具体的例子
来演示一下这个final
我们看在定义Base1的时候呢
加了一个final来修饰
那就说明
定义Base1的人
指明了Base1
是不能派生出新类的
不能当做基类去继承的
那接下来呢
如果在程序的其他部分
我们尝试硬是要从Base1类
派生出Derived类
那么编译器就会报错
因为Base1是final
是不允许被继承的
再看下面这个例子
在Base2里面呢
我们声明这个函数f的时候
这个虚函数f
把它声明为final了
那就说明这个函数
是不能被覆盖的
那接下来
看我们虽然可以从Base2
派生出Derived2
但是
在Derived2这个类中如果尝试
重新定义一个函数f
去覆盖那个继承而来的虚函数
编译器也会报错
因为Base2里面的函数f
是final 是不允许被覆盖的
这个override和final
虽然是C++11提供的新功能
但是它并不是语言的关键字
也就是说
它只在特定的地方
具有特定的含义
在其他地方
如果你用override和final
做一般的标志符使用呢
也仍然是可以的
但是建议大家不要这样用
你就当它是关键字
不要在别的地方
拿它当标志符用
这样免得你的程序可读性
受到影响
-导学
--导学
-继承的基本概念和语法
-第七章 继承与派生--继承的基本概念和语法习题
-继承方式
-第七章 继承与派生--继承方式
-基类与派生类类型转换
-第七章 继承与派生--基类与派生类类型转换
-派生类的构造和析构
--派生类的构造函数
--派生类的析构函数
--第七章 继承与派生--派生类的构造和析构
-派生类成员的标识与访问
--虚基类
-第七章 继承与派生--派生类成员的标识与访问
-小结
--小结
-综合实例
--第七章综合实例
-实验七
--实验七
-导学
--导学
-第八章 多态性--导学
-运算符重载
--运算符重载的规则
-第八章 多态性--运算符重载
-虚函数
--虚函数
--虚析构函数
--虚表与动态绑定
-第八章 多态性--虚函数
-抽象类
--抽象类
--第八章 多态性--抽象类
-override与final
-第八章 多态性--override与final
-小结
--第八章小结
-综合实例
--第八章综合实例
-实验八
--实验八
- 第八章讲义
-导学
--导学
-模板
--函数模板
--类模板
-第九章 模板与群体数据--模板
-线性群体
--线性群体的概念
-第九章 模板与群体数据--线性群体
-数组
--数组类模板
-链表
--链表类模板
-第九章 模板与群体数据--链表
-栈
--栈类模板
--栈类模板课后习题
--例9-9 栈的应用课后习题
-队列
--队列类模板
-第九章 模板与群体数据--队列
-排序
--排序概述
--插入排序
--选择排序
--交换排序
-第九章 模板与群体数据--排序
-查找
--查找
--查找课后习题
-小结
--小结
-综合实例
--综合实例
-实验九
--实验九
- 第九章讲义
-导学
--导学
-泛型程序设计及STL的结构
--STL简介
-第十章 泛型程序设计与C++标准模板库--泛型程序设计及STL的结构
-迭代器
--迭代器
-第十章 泛型程序设计与C++标准模板库--迭代器
-容器的基本功能与分类
-第十章 泛型程序设计与C++标准模板库--容器的基本功能与分类
-顺序容器
--顺序容器的特征
--第十章 泛型程序设计与C++标准模板库--顺序容器
-关联容器
--集合
--映射
-第十章 泛型程序设计与C++标准模板库--关联容器
-函数对象
--函数对象
--函数适配器
-算法
--算法
-小结
--第十章小结
-综合实例
--综合实例
-实验十
--实验十
- 第十章讲义
-导学
--导学
-I/O流的概念及流类库结构
-第十一章 流类库与输入/输出--I/O流的概念及流类库结构
-输出流
--输出流概述
--向文本文件输出
--向二进制文件输出
--向字符串输出
-第十一章 流类库与输入/输出--输出流
-输入流
--输入流概述
--输入流应用举例
--从字符串输入
-第十一章 流类库与输入/输出--输入流
-输入/输出流
--输入/输出流
-第十一章 流类库与输入/输出--输入/输出流
-小结
--小结
-综合实例
--综合实例
-实验十一
--实验十一
- 第十一章讲义
-导学
--第12章导学
-异常处理的思想与程序实现
-第十二章 异常处理--异常处理的思想与程序实现
-异常处理中的构造与析构
-第十二章 异常处理--异常处理中的构造与析构
-标准程序库异常处理
-第十二章 异常处理--标准程序库异常处理
-小结
--第12章小结
-综合实例
--综合实例
-实验十二
--实验十二
- 第十二章讲义