《深入理解C++11:C++ 11新特性解析与应用》——2.10 final/override控制

简介:

2.10 final/override控制

类别:部分人

在了解C++11中的final/override关键字之前,我们先回顾一下C++关于重载的概念。简单地说,一个类A中声明的虚函数fun在其派生类B中再次被定义,且B中的函数fun跟A中fun的原型一样(函数名、参数列表等一样),那么我们就称B重载(overload)了A的fun函数。对于任何B类型的变量,调用成员函数fun都是调用了B重载的版本。而如果同时有A的派生类C,却并没有重载A的fun函数,那么调用成员函数fun则会调用A中的版本。这在C++中就实现多态。

在通常情况下,一旦在基类A中的成员函数fun被声明为virtual的,那么对于其派生类B而言,fun总是能够被重载的(除非被重写了)。有的时候我们并不想fun在B类型派生类中被重载,那么,C++98没有方法对此进行限制。我们看看下面这个具体的例子,如代码清单2-23所示。

image
image

在代码清单2-23中,我们的基础类MathObject定义了两个接口:Arith和Print。类Printable则继承于MathObject并实现了Print接口。接下来,Add2和Mul3为了使用MathObject的接口和Printable的Print的实现,于是都继承了Printable。这样的类派生结构,在面向对象的编程中非常典型。不过倘若这里的Printable和Add2是由两个程序员完成的,Printable的编写者不禁会有一些忧虑,如果Add2的编写者重载了Print函数,那么他所期望的统一风格的打印方式将不复存在。

对于Java这种所有类型派生于单一元类型(Object)的语言来说,这种问题早就出现了。因此Java语言使用了final关键字来阻止函数继续重写。final关键字的作用是使派生类不可覆盖它所修饰的虚函数。C++11也采用了类似的做法,如代码清单2-24所示的例子。

image

在代码清单2-24中,派生于Object的Base类重载了Object的fun接口,并将本类中的fun函数声明为final的。那么派生于Base的Derived类对接口fun的重载则会导致编译时的错误。同理,在代码清单2-23中,Printable的编写者如果要阻止派生类重载Print函数,只需要在定义时使用final进行修饰就可以了。

读者可能注意到了,在代码清单2-23及代码清单2-24两个例子当中,final关键字都是用于描述一个派生类的。那么基类中的虚函数是否可以使用final关键字呢?答案是肯定的,不过这样将使用该虚函数无法被重载,也就失去了虚函数的意义。如果不想成员函数被重载,程序员可以直接将该成员函数定义为非虚的。而final通常只在继承关系的“中途”终止派生类的重载中有意义。从接口派生的角度而言,final可以在派生过程中任意地阻止一个接口的可重载性,这就给面向对象的程序员带来了更大的控制力。

在C++中重载还有一个特点,就是对于基类声明为virtual的函数,之后的重载版本都不需要再声明该重载函数为virtual。即使在派生类中声明了virtual,该关键字也是编译器可以忽略的。这带来了一些书写上的便利,却带来了一些阅读上的困难。比如代码清单2-23中的Printable的Print函数,程序员无法从Printable的定义中看出Print是一个虚函数还是非虚函数。另外一点就是,在C++中有的虚函数会“跨层”,没有在父类中声明的接口有可能是祖先的虚函数接口。比如在代码清单2-23中,如果Printable不声明Arith函数,其接口在Add2和Mul3中依然是可重载的,这同样是在父类中无法读到的信息。这样一来,如果类的继承结构比较长(不断地派生)或者比较复杂(比如偶尔多重继承),派生类的编写者会遇到信息分散、难以阅读的问题(虽然有时候编辑器会进行提示,不过编辑器不是总是那么有效)。而自己是否在重载一个接口,以及自己重载的接口的名字是否有拼写错误等,都非常不容易检查。

在C++11中为了帮助程序员写继承结构复杂的类型,引入了虚函数描述符override,如果派生类在虚函数声明时使用了override描述符,那么该函数必须重载其基类中的同名函数,否则代码将无法通过编译。我们来看一下如代码清单2-25所示的这个简单的例子。

image

在代码清单2-25中,我们在基类Base中定义了一些virtual的函数(接口)以及一个非virtual的函数Print。其派生类DerivedMid中,基类的Base的接口都没有重载,不过通过注释可以发现,DerivedMid的作者曾经想要重载出一个“void VNeumann(double g)”的版本。这行注释显然迷惑了编写DerivedTop的程序员,所以DerivedTop的作者在重载所有Base类的接口的时候,犯下了3种不同的错误:

image

如果没有override修饰符,DerivedTop的作者可能在编译后都没有意识到自己犯了这么多错误。因为编译器对以上3种错误不会有任何的警示。这里override修饰符则可以保证编译器辅助地做一些检查。我们可以看到,在代码清单2-25中,DerivedTop作者的4处错误都无法通过编译。

此外,值得指出的是,在C++中,如果一个派生类的编写者自认为新写了一个接口,而实际上却重载了一个底层的接口(一些简单的名字如get、set、print就容易出现这样的状况),出现这种情况编译器还是爱莫能助的。不过这样无意中的重载一般不会带来太大的问题,因为派生类的变量如果调用了该接口,除了可能存在的一些虚函数开销外,仍然会执行派生类的版本。因此编译器也就没有必要提供检查“非重载”的状况。而检查“一定重载”的override关键字,对程序员的实际应用则会更有意义。

还有值得注意的是,如我们在第1章中提到的,final/override也可以定义为正常变量名,只有在其出现在函数后时才是能够控制继承/派生的关键字。通过这样的设计,很多含有final/override变量或者函数名的C++98代码就能够被C++编译器编译通过了。但出于安全考虑,建议读者在C++11代码中应该尽可能地避免这样的变量名称或将其定义在宏中,以防发生不必要的错误。

相关文章
|
6月前
|
Ubuntu API C++
C++标准库、Windows API及Ubuntu API的综合应用
总之,C++标准库、Windows API和Ubuntu API的综合应用是一项挑战性较大的任务,需要开发者具备跨平台编程的深入知识和丰富经验。通过合理的架构设计和有效的工具选择,可以在不同的操作系统平台上高效地开发和部署应用程序。
264 11
|
存储 负载均衡 算法
基于 C++ 语言的迪杰斯特拉算法在局域网计算机管理中的应用剖析
在局域网计算机管理中,迪杰斯特拉算法用于优化网络路径、分配资源和定位故障节点,确保高效稳定的网络环境。该算法通过计算最短路径,提升数据传输速率与稳定性,实现负载均衡并快速排除故障。C++代码示例展示了其在网络模拟中的应用,为企业信息化建设提供有力支持。
355 15
|
机器学习/深度学习 文字识别 监控
安全监控系统:技术架构与应用解析
该系统采用模块化设计,集成了行为识别、视频监控、人脸识别、危险区域检测、异常事件检测、日志追溯及消息推送等功能,并可选配OCR识别模块。基于深度学习与开源技术栈(如TensorFlow、OpenCV),系统具备高精度、低延迟特点,支持实时分析儿童行为、监测危险区域、识别异常事件,并将结果推送给教师或家长。同时兼容主流硬件,支持本地化推理与分布式处理,确保可靠性与扩展性,为幼儿园安全管理提供全面解决方案。
555 3
|
人工智能 API 开发者
HarmonyOS Next~鸿蒙应用框架开发实战:Ability Kit与Accessibility Kit深度解析
本书深入解析HarmonyOS应用框架开发,聚焦Ability Kit与Accessibility Kit两大核心组件。Ability Kit通过FA/PA双引擎架构实现跨设备协同,支持分布式能力开发;Accessibility Kit提供无障碍服务构建方案,优化用户体验。内容涵盖设计理念、实践案例、调试优化及未来演进方向,助力开发者打造高效、包容的分布式应用,体现HarmonyOS生态价值。
800 27
|
供应链 项目管理 容器
深入探索 BPMN、CMMN 和 DMN:从定义到应用的全方位解析
在当今快速变化的商业环境中,对象管理组织(OMG)推出了三种强大的建模标准:BPMN(业务流程模型和符号)、CMMN(案例管理模型和符号)和DMN(决策模型和符号)。它们分别适用于结构化流程管理、动态案例处理和规则驱动的决策制定,并能相互协作,覆盖更广泛的业务场景。BPMN通过直观符号绘制固定流程;CMMN灵活管理不确定的案例;DMN以表格形式定义清晰的决策规则。三者结合可优化企业效率与灵活性。 [阅读更多](https://example.com/blog)
深入探索 BPMN、CMMN 和 DMN:从定义到应用的全方位解析
|
数据采集 机器学习/深度学习 存储
可穿戴设备如何重塑医疗健康:技术解析与应用实战
可穿戴设备如何重塑医疗健康:技术解析与应用实战
617 4
|
存储 弹性计算 安全
阿里云服务器ECS通用型规格族解析:实例规格、性能基准与场景化应用指南
作为ECS产品矩阵中的核心序列,通用型规格族以均衡的计算、内存、网络和存储性能著称,覆盖从基础应用到高性能计算的广泛场景。通用型规格族属于独享型云服务器,实例采用固定CPU调度模式,实例的每个CPU绑定到一个物理CPU超线程,实例间无CPU资源争抢,实例计算性能稳定且有严格的SLA保证,在性能上会更加稳定,高负载情况下也不会出现资源争夺现象。本文将深度解析阿里云ECS通用型规格族的技术架构、实例规格特性、最新价格政策及典型应用场景,为云计算选型提供参考。
|
人工智能 自然语言处理 算法
DeepSeek大模型在客服系统中的应用场景解析
在数字化浪潮下,客户服务领域正经历深刻变革,AI技术成为提升服务效能与体验的关键。DeepSeek大模型凭借自然语言处理、语音交互及多模态技术,显著优化客服流程,提升用户满意度。它通过智能问答、多轮对话引导、多模态语音客服和情绪监测等功能,革新服务模式,实现高效应答与精准分析,推动人机协作,为企业和客户创造更大价值。
992 5
|
机器学习/深度学习 JSON 算法
淘宝拍立淘按图搜索API接口系列的应用与数据解析
淘宝拍立淘按图搜索API接口是阿里巴巴旗下淘宝平台提供的一项基于图像识别技术的创新服务。以下是对该接口系列的应用与数据解析的详细分析
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

推荐镜像

更多
  • DNS
  • 下一篇
    开通oss服务