Effective C++ 改善程序与设计的55个具体做法笔记与心得 5

五. 实现

26. 尽可能延后变量定义式的出现时间

请注意:
‌‌‌‌  尽可能延后变量定义式的出现,这样做可增加程序的清晰度并改善程序效率。

解释

  1. 增加程序的清晰度:这样可以让读者在第一时间内更好地理解变量的用途。变量定义在第一次使用的地方,可以清晰地看到其类型和初始值。如果变量在几页代码之前就已被定义,那么读者可能需要花费更多的时间在代码文件中寻找其定义和初次使用的地方,这对代码的阅读是不友好的。

  2. 改善程序效率:如果变量在定义的时候就初始化,那么可能会对性能产生影响,因为这实际上创建了变量并分配了内存,而在很多情况下,这都可能是不必要的。与之相反,将变量的定义和初始化处理延后到真正需要它的地方,这可以避免不必要的计算和内存分配,从而可以提高程序效率。

  3. 避免不必要的副作用:许多时候,对象的构造函数可能会产生副作用。在对象真正需要之前,其构造函数可能产生一些不必要的副作用。因此,将变量的定义延后,直到真正需要这个对象,可以帮助我们避免这些可能的问题。

‌‌‌‌  综上,尽可能地延后变量定义式的出现,可以使代码更容易阅读和理解,也有助于提高代码质量和执行效率。

27. 尽量少做转型动作

请注意

  • 如果可以,尽量避免转型,特别是在注重效率的代码中避免 dynamic_casts。如果有个设计需要转型动作,试着发展无需转型的替代设计。
  • 如果转型是必须的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需要将转型放进他们自己的代码内。
  • 宁可使用C+±style(新式)转型,不要使用旧式转型。前者很容易辨识出来,而且也比较有着分门别类的职掌。

解释

‌‌‌‌  在编程中,类型转换的滥用可能导致代码难以理解和维护,而且往往可以通过改进设计来避免。这是因为转型往往会破坏类型安全,增加运行时错误的机会。

‌‌‌‌  在某些情况下,如果转型确实是必要的,有几种方式可以减轻其可能带来的负面影响:

  1. 封装转型操作:如果能够将转型隐藏在函数或方法中,那么调用者就无需在他们自己的代码中进行转型。这有助于使代码保持清晰和一致。

  2. 使用C++风格的转型:C++风格的转型(如static_cast、dynamic_cast、const_cast和reinterpret_cast)比C风格的转型(如(int) x或者int(x))更具可读性。它们明确指示了转型的方式和意图,使转型更容易被发现和理解,从而降低代码错误的概率。

  3. 避免使用dynamic_cast:dynamic_cast在运行时检查转换的有效性,这可能会产生较大的性能开销。如果能够改变设计以使用其他类型的转型,或者完全避免转型,那么代码可能会运行得更快。

  4. 提供无需转型的替代设计:如果设计需要频繁转型,这可能表明设计可以进一步改进。试图找到无需转型操作的设计方案。

‌‌‌‌  总的来说,应该尽量避免使用转型,如果无法避免,则应选择最安全并易于理解的方式进行。

28. 避免返回handles指向对象内部成分

请注意
‌‌‌‌  避免返回handles(包括references、指针、迭代器)指向对象内部。遵守这个条款可增加封装性,帮助const成员函数的行为像个const,并将发生“虚吊号码牌”的可能性降至最低。

解释
‌‌‌‌  返回指向对象内部元素的引用、指针或迭代器实质上是在破坏封装,使得对象的内部结构暴露给外部。这样做可能会带来几个问题:

  1. 破坏封装:如果一个类的实现细节(即它的内部结构)被暴露给类的用户,那么类的用户可能就会依赖于那些细节。这样一来,如果这些实现细节在将来发生改变,那么使用该类的代码也就必须进行改动。

  2. 破坏const成员函数:如果一个const成员函数返回一个指向其数据成员的引用或者指针,那么即使这个引用或指针被声明为const,编译器也只能阻止通过这个引用或者指针修改数据成员,而不能阻止通过将其强制转换为非const引用或者指针来修改数据成员。

  3. 更高的"悬挂引用"风险:如果返回的引用、指针或迭代器是指向对象的局部变量或临时变量,那么这个引用、指针或迭代器可能就会变成一个"悬挂引用",也就是说,它指向的对象可能在它被使用之前就已经被销毁了。

‌‌‌‌  所以,为了保证良好的封装性,并保护数据的完整性,避免返回指向对象内部的handles是个好的做法。如果需要返回对象的状态或者内部数据,可以考虑返回这些数据的副本,或者提供一些获取和设置这些数据的public成员函数。

29. 为“异常安全”而努力是值得的

请记住

  • 异常安全函数即使发生异常也不会泄露资源或允许任何数据结构破坏。这样的函数区分为三种可能的保证:基本型、强烈型、不抛异常型。
  • “强烈保证”往往能够以copy-and-swap实现出来,但”强烈保证”并非对所有函数都可实现或具备现实意义。
  • 函数提供的“异常安全保证”通常最高只等于其所调用之各个函数的“异常安全保证”中的最弱者。

解释
‌‌‌‌  在编程中,我们确实把异常安全的函数分为三类:基本保证、强烈保证和不抛异常保证。这三类保证分别对应了不同程度的异常安全性。

  1. 基本保证:这意味着函数抛出异常后,程序的内部状态仍然保持一致,不会有内存泄漏或者数据结构被破坏的情况,但特定的操作可能未能完成。

  2. 强烈保证:这意味着函数抛出异常后,程序回到其调用函数之前的状态,也就是说,如果操作不能完成,那么就像根本没有进行过操作一样。这种保证通常通过事务(transaction)的概念进行实现,即将可能失败的操作进行拷贝和交换,确保在原子操作中完成。

  3. 不抛异常保证:这是最高级别的保证,意味着函数保证不抛出任何异常。

‌‌‌‌  你提到的“函数提供的异常安全保证通常最高只等于其所调用之各个函数的异常安全保证中的最弱者”,这个说法也是正确的。这是因为,如果一个函数f调用了另一个提供了较弱异常安全保证的函数g,那么,f的异常安全性就会受到g的影响。

‌‌‌‌  因此,在设计和实现时,应当尽可能使得函数在遇到异常时也能保证安全,以防数据丢失或程序崩溃。同时,也要理解和注意函数间异常安全级别的影响关系。

30. 透彻了解inlining的里里外外

请记住

  • ‌‌‌‌ 将大多数inlining限制在小型、被频繁调用的函数身上。这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化,使程序的速度提升机会最大化。
  • ‌‌‌‌ 不要只因为function templates出现在头文件,就将他们声明为inline

解释
‌‌‌‌  你的理解是正确的。内联函数(inlining)是一种优化技术,通过把函数调用替换为函数体的内容来消除函数调用的开销,提高程序运行速度。然而,滥用内联函数会导致一些问题:

  1. 代码膨胀:过度的内联可能导致代码膨胀,因为函数体会被复制到每个调用它的地方。如果一个大型函数被频繁调用并且它被声明为内联的,那么这会导致最终的程序体积较大。

  2. 调试和更新的困难:内联函数的改变需要重新编译所有使用到它的代码,这会使得调试和更新过程复杂并且增加了维护的工作量。

  3. 性能没有明显提升:函数的内联并不一定能带来预期的性能提升。对于大型函数,由于代码膨胀导致的缓存未命中(cache miss)可能抵消了由于去除函数调用而带来的性能提升。所以,相比较大型函数,较小的、频繁调用的函数更适合声明为内联。

‌‌‌‌  关于函数模板(function templates),由于它们通常定义在头文件中,编译器可能会自动将它们视为内联,即使你没有明确声明。然而,不应该仅仅因为函数模板在头文件中就设为内联,因为这可能导致之前提到的代码膨胀和维护问题。

‌‌‌‌  总的来说,我们应当谨慎地使用内联,在明确知道它会带来性能提升的情况下,才将函数设置为内联。

31. 将文件间的编译依存关系降至最低

请记住

  • 支持“编译依存性最小化”的一般构想是:相依于声明式,不要相依于定义式。基于此构想的两个手段是Handless classes 和 Interface classes
  • 程序库头文件应该以“完全且仅有声明式”的形式存在。这种做法不论是否涉及templates都适用。

解释

‌‌‌‌  最小化编译依赖性。事实上,这是一种良好编程习惯,可以大大改善代码的维护性和可扩展性。

‌‌‌‌  当我们说“依赖于声明,而不是依赖于定义”时,我们的意思是,我们应该把对特定代码块的依赖性限制在它的接口(即它的声明)上,而不是它的实现(即它的定义)上。

‌‌‌‌  这样做的好处是,让我们能够更改或优化一个类或函数的实现,而不影响任何依赖于它的代码。这也解耦了我们的代码,使得每个部分更容易独立测试和修改。

‌‌‌‌  Handle classes和Interface classes,都能有效地实现这个原则。

  1. Handle classes(又叫做Pimpl,pointer to implementation):这种技术隐藏了类的实现细节,只在头文件中暴露一个接口。这样,即使类的实现发生了改变,也不会影响到使用这个类的其他代码。

  2. Interface classes:这个方法使用纯虚函数定义一个接口,然后通过不同的子类来提供不同的实现。这样,客户端代码只依赖于接口,并不关心具体的实现。

‌‌‌‌  无论你是在使用普通类还是模板,遵循“最小化编译依赖性”的原则,你的头文件应该只包含完成的声明,而不是定义。这样可以减少不必要的编译依赖,使代码更容易维护。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/32700.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

黑马苍穹外卖6 清理redis缓存+Spring Cache+购物车的增删改查

缓存菜品 后端服务都去查询数据库,对数据库访问压力增大。 解决方式:使用redis来缓存菜品,用内存比磁盘性能更高。 key :dish_分类id String key “dish_” categoryId; RestController("userDishController") RequestMapping…

批量重命名神器揭秘:一键实现文件夹随机命名,自定义长度轻松搞定!

在数字化时代,我们经常需要管理大量的文件夹,尤其是对于那些需要频繁更改或整理的文件来说,给它们进行批量重命名可以大大提高工作效率。然而,传统的重命名方法既繁琐又耗时,无法满足高效工作的需求。今天,…

transformers datasets

☆ 问题描述 在进行自然语言处理项目时,经常需要加载和处理不同的数据集。为了简化这一过程,我们可以使用datasets库来方便地加载、切分、查看和处理数据。本解决方案提供了如何使用datasets库加载、查看和处理数据的详细示例,包括如何加载在…

Java中的类加载器与热部署技术详解

Java中的类加载器与热部署技术详解 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!在软件开发中,特别是在大型应用和服务的开发过程中,类…

ic基础|功耗篇03:ic设计人员如何在代码中降低功耗?一文带你了解行为级以及RTL级低功耗技术

大家好,我是数字小熊饼干,一个练习时长两年半的ic打工人。我在两年前通过自学跨行社招加入了IC行业。现在我打算将这两年的工作经验和当初面试时最常问的一些问题进行总结,并通过汇总成文章的形式进行输出,相信无论你是在职的还是…

【C++PCL】点云处理稳健姿态估计配准

作者:迅卓科技 简介:本人从事过多项点云项目,并且负责的项目均已得到好评! 公众号:迅卓科技,一个可以让您可以学习点云的好地方 重点:每个模块都有参数如何调试的讲解,即调试某个参数对结果的影响是什么,大家有问题可以评论哈,如果文章有错误的地方,欢迎来指出错误的…

TI毫米波雷达可以用串口调试助理来获取原始数据吗?

摘要:本文介绍一下如何使用普通的串口调试助理来读取到AWR1843毫米波雷达的数据的。 使用的硬件如下图所示。 软件就是普通的串口助理,我用的是SSCOM,其他串口助理也是可以的,核心作用其实就是发送一行行的指令而已。 操作方法&am…

【免费】中国电子学会2024年03月份青少年软件编程Python等级考试试卷二级真题(含答案)

2024-03 Python二级真题 分数:100 题数:37 测试时长:60min 一、单选题(共25题,共50分) 1. 期末考试结束了,全班的语文成绩都储存在列表score中,班主任老师请小明找到全班最高分,小明准备用…

20240623(26.0) 重要财经新闻

财经关注 ► 券商中国:北交所于6月21日晚间受理了3家企业的IPO申请。6月20日晚间,沪深交易所各受理了1家IPO申请。这也意味着,三大交易所IPO受理全部恢复。与此同时,三大交易所IPO上市委会议也已经全部重启。 ► 全球多地近期遭遇…

Linux开发讲课9--- Linux的IPC机制-内存映射(Memory Mapping)

Linux的IPC(Inter-Process Communication,进程间通信)机制是多个进程之间相互沟通的方法,它允许不同进程之间传播或交换信息。Linux支持多种IPC方式,包括但不限于: 管道(Pipe)&#…

微信小程序学习(六):常用原生 API

🔗API官方文档 1、网络请求 wx.request({// 接口地址,仅为示例,并非真实的接口地址url: example.php,// 请求的参数data: { x: },// 请求方式 GET|POST|PUT|DELETEmethod: GET,success (res) {console.log(res.data)},fail(err) {console.…

msvcp140.dll丢失的解决方法,msvcp140.dll丢失下载办法

一、msvcp140.dll丢失或损坏的影响 系统更新影响 系统更新是导致msvcp140.dll丢失或损坏的常见原因之一。在自动更新过程中,可能会引入与现有应用程序不兼容的DLL版本,从而引发错误。根据用户反馈和技术支持数据,大约15%的msvcp140.dll问题…

2-3KW户储、家储逆变器设计资料

储能电源方案双向逆变器板资料,原理文件,PCB文件,源代码,bom清单。 bom表: PCB: 变压器电感 2-3KW户储、家储逆变器设计通常需要考虑以下几个方面: 输入电压范围:逆变器需要能够适应…

接口性能优化方法总结

接口性能优化是后端开发人员经常碰到的一道面试题,因为它是一个跟开发语言无关的公共问题。 这个问题既可以很简单,也可以相当复杂。 导致接口性能问题的原因多种多样,不同项目的不同接口,其原因可能各不相同。 下面列举几种常…

2024-6-18(沉默Spring,Springboot)

1.Spring小结 我们最后再来体会一下用 Spring 创建对象的过程: 通过 ApplicationContext 这个 IoC 容器的入口,用它的两个具体的实现子类,从 class path 或者 file path 中读取数据,用 getBean() 获取具体的 bean instance。 那…

oracle发送https请求

参照 https://docs.oracle.com/cd/E11882_01/appdev.112/e40758/u_http.htm#i1025869 https://docs.oracle.com/cd/E11882_01/network.112/e40393/asowalet.htm#ASOAG160 https://docs.oracle.com/cd/E11882_01/appdev.112/e40758/d_networkacl_adm.htm#ARPLS148 https://d…

Tailwindcss 提取组件

背景 随着项目的发展&#xff0c;您不可避免地会发现自己需要重复使用常用样式&#xff0c;以便在许多不同的地方重新创建相同的组件。这在小组件&#xff08;如按钮、表单元素、徽章等&#xff09;中最为明显。在我的项目中是图表标题样式如下&#xff1a; <div class&qu…

基于Openmv的色块识别代码及注意事项

在给出代码之前我先说注意事项以及需要用到的函数 1、白平衡和自动增益的关闭 打开白平衡和自动增益会影响颜色识别的效果&#xff0c;具体影响体现在可能使你颜色阈值发生改变 关闭代码如下 sensor.set_auto_gain(False) #关闭自动增益 sensor.set_whitebal(False) …

喜报!极限科技新获得一项国家发明专利授权:“搜索数据库的正排索引处理方法、装置、介质和设备”

近日&#xff0c;极限数据&#xff08;北京&#xff09;科技有限公司&#xff08;简称&#xff1a;极限科技&#xff09;新获得一项国家发明专利授权&#xff0c;专利名为 “搜索数据库的正排索引处理方法、装置、介质和设备”&#xff0c;专利号&#xff1a;ZL 2024 1 0479400…

嵌入式软件面试记录(6)

1.关键字 extem 有什么作用? 答&#xff1a;修饰变量或函数&#xff0c;在当前文件引用另一个文件中定义的变量或者函数。 2.局部变量能否和全局变量重名&#xff1f; 答&#xff1a;可以重名&#xff0c;局部变量会屏蔽全局变量。 3.typedef和#define的区别? 答&#xff1a;…