《C++编程规范——101条规则、准则与最佳实践》笔记008

C++编程规范

C++ coding standards

Author
Herb Sutter 《Exceptional C++ Style》 《Exceptional C++》 《More Exceptional C++》
Andrei Alexandrescu 《Modern C++ Design》 Loki

设计风格(适用面比一个特定的类或者函数更广的原则和实践)

复杂性啊,愚人对你视而不见,实干家受你所累。有些人避而远之。惟智者能够善加消除。 ——Alan Perlis


我知道,但是却又忘记了Hoare的至理名言:不成熟的优化是程序设计中的万恶之源。 ——Donald Knuth

简单和清晰之间的平衡、避免不成熟的优化、避免不成熟的劣化,不仅适用于函数编写的层次,而且适用于类和模块设计权衡的更大范围,适用于更深的应用程序架构决策。
依赖性管理是软件工程的一个基础,任意选择一个优秀的软件工程技术,无论选择哪一个,它都是在想尽办法减少依赖性。
  • 继承?是为了使所编写的代码使用不依赖于实际派生类的基类。
  • 尽量减少全局变量?是为了减少因可见范围太大的数据所产生的远距离依赖。
  • 抽象?是为了消除处理概念的代码和实现它们的代码之间的依赖。
  • 信息隐藏?是为了使客户代码不依赖实体的实现细节。
依赖性管理的一个相关问题还反映在避免使用共享状态中,反映在应用信息隐藏,以及其他之中。
最有价值:第6条——正确、简单和清晰第一。

第8条 不要进行不成熟的优化

摘要
拉丁谚语,快马无需鞭策:不成熟优化的诱惑非常大,而它的无效性也同样严重。优化的第一原则就是:不要优化。优化的第二原则(仅适用于专家)是:还是不要优化。再三测试,而后优化。
讨论

不成熟的优化是万恶之源。——Donald Knuth
另一方面,我们不能忽视效率。——John Bentley

不成熟的优化:以性能为名,使设计或代码更加复杂,从而导致可读性更差,但是并没有经过验证的性能需求(比如实际的度量数据和与目标的比较结果)作为正当理由,因此本质上对程序没有真正的好处。毫无必要而且无法度量的优化行为其实根本不能使程序运行得更快。

让一个正确的程序更快速,比让一个快速的程序正确,要容易得太多、太多。

因此,默认时,不要把注意力集中在如何使代码更快上;首先关注的应该是使代码尽可能地清晰和易读。清晰的代码更容易正确编写,更容易理解,更容易重构——当然也更容易优化。使事情复杂的行为,包括优化,总是以后再进行的——而且只在必要的时候进行。
不成熟的优化经常并不能使程序更快:


  • 一方面,程序员在估计哪些代码应该更快或更小,以及代码中哪里会成为瓶颈上名声很臭。考虑一些事实,现代计算机都具有极为复杂的计算模型,经常是几个流水线处理单元并行工作,深高速缓存层次结构,猜测执行,分支预测……这还只是CPU芯片。在硬件之上,编译器也在尽其所能地猜测,将源代码转换成最能发掘硬件潜力的机器码。而在这些复杂的结构之上,还有“程序员的猜测”。所以,如果只是猜测的话,那些目标不明确的微观优化很难有机会显著地改善代码。因此,优化之前必须进行度量;而度量之前必须确定优化的目标。在需求得到验证之前,注意力应该放在头号优先的事情上——为人编写代码。(当有什么人要求你进行优化的时候,请进行需求验证。)

猜测执行,speculative execution,程序设计和计算机系统体系结构中的一种优化措施。在现代流水线微处理器中,使用猜测执行降低条件分支指令的代价。遇到条件分支指令时,处理器猜测最有可能转向的分支,并立即从此点开始执行。如果猜测不正确,此点之后的计算全部放弃。由于在下一指令知道之前,所涉及的流水线是休眠的,所以这种计算的代价很低。
  • 另一方面,在现代程序中,许多操作越来越不受CPU的限制。它们可能更受内存的限制、网络的限制、硬盘的限制,需要等待Web Service,或等待数据库。即使在最好的情况下,优化这些操作的应用程序代码,也只不过能使等待操作更快。这也意味着程序员浪费了宝贵的时间去改善没有必要改善的地方,却没有进行必要的有价值的改善。
当然,迟早需要优化某些代码。首先要考虑算法优化,并尝试将优化封装和模块化,然后在注释中清楚地说明优化的原因并列出所用算法作为参考。
初学者常犯的一个错误,就是编写新代码时着迷于进行过度优化(而且充满自信),却牺牲了代码的可理解性。这常常会产生大杂烩代码,这种代码即使开始时是正确的,也非常难以阅读和修改。
通过引用传递,优先调用前缀形式的++和–,和使用很自然地从指尖流出的惯用法,都不属于不成熟的优化。这些都不是不成熟的优化,而是在避免不成熟的劣化。
示例
inline悖论(不成熟的微观优化所带来的隐性代价)

分析器(profiler)能够通过函数的命中计数出色地告诉我们哪些函数应该但是没有标记为inline;然而,分析器在寻找哪些已经标记为inline但是不应该标记方面,却极不擅长。太多的程序员习惯以优化的名义“将inline作为默认选择”,这几乎总是以更高的耦合性为代价,而换来的好处到底如何却很可疑。
组织和策略问题 1 第0 不要拘泥于小节(又名:了解哪些东西不应该标准化) 2 第1 在高警告级别干净利落地进行编译 4 第2 使用自动构建系统 7 第3 使用版本控制系统 8 第4 做代码审查 9设计风格 11 第5 一个实体应该只有一个紧凑的职责 12 第6 正确、简单和清晰第一 13 第7 编程中应知道何时和如何考虑可伸缩性 14 第8 不要进行不成熟的优化 16 第9 不要进行不成熟的劣化 18 第10 尽量减少全局和共享数据 19 第11 隐藏信息 20 第12 懂得何时和如何进行并发性编程 21 第13 确保资源为对象所拥有。使用显式的RAII和智能指针 24 编程风格 27 第14 宁要编译时和连接时错误,也不要运行时错误 28 第15 积极使用const 30 第16 避免使用宏 32 第17 避免使用“魔数” 34 第18 尽可能局部地声明变量 35 第19 总是初始化变量 36 第20 避免函数过长,避免嵌套过深 38 第21 避免跨编译单元的初始化依赖 39 第22 尽量减少定义性依赖。避免循环依赖 40 第23 头文件应该自给自足 42 第24 总是编写内部#include保护符,决不要编写外部#include保护符 43 函数与操作符 45 第25 正确地选择通过值、(智能)指针或者引用传递参数 46 第26 保持重载操作符的自然语义 47 第27 优先使用算术操作符和赋值操作符的标准形式 48 第28 优先使用++和--的标准形式。优先调用前缀形式 50 第29 考虑重载以避免隐含类型转换 51 第30 避免重载&&、||或 ,(逗号) 52 第31 不要编写依赖于函数参数求值顺序的代码 54 类的设计与继承 55 第32 弄清所要编写的是哪种类 56 第33 用小类代替巨类 57 第34 用组合代替继承 58 第35 避免从并非要设计成基类的类中继承 60 第36 优先提供抽象接口 62 第37 公用继承即可替换性。继承,不是为了重用,而是为了被重用 64 第38 实施安全的覆盖 66 第39 考虑将虚拟函数声明为非公用的,将公用函数声明为非虚拟的 68
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值