读书笔记:《C++ 编程规范——101条规则、准则与最佳实践》(2)设计风格

豆瓣链接:《C++ 编程规范——101条规则、准则与最佳实践》


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


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


依赖性管理是软件工程的一个基础,也是贯穿本书不断出现的主题。

任意优秀的软件工程技术,都在想尽办法减少依赖性:

  • 继承?是为了代码的使用不依赖于实际派生类的基类(其实这句话我没读明白……)
  • 减少全局变量?是为了避免产生大量的远距离依赖。
  • 抽象?是为了消除表示概念的代码与实现功能的代码间的依赖。
  • 信息隐藏?是为了客户代码不依赖于实现细节。
  • ……

第 5 条:一个实体只应有一个紧凑的职责

这一点无需多言,书中给出了一个反例——realloc

在标准 C 语言中,realloc 是一个臭名朝著的不良设计:

  • 如果传入 NULL,它就像 malloc 一样分配新的内存空间。
  • 如果大小参数为 0,它就承担 free() 的任务释放内存空间。
  • 如果可行,它就就地重新分配,反之就到其他地方分配。

这个函数不易扩展,人们普遍认为它是一个目光短浅的失败设计。

书中提到的另一个不良设计的例子是 std::basic_string,一个臃肿的巨大类,这里不进行介绍了。


第 6 条:正确、简单和清晰第一

著名的 KISS 原则(Keep It Simple Software):

  • 质量优于速度
  • 简单优于复杂
  • 清晰优于机巧
  • 安全优于隐患

这是书中提到的名言之一:

程序必须为阅读它的人儿编写,只是顺便利于机器执行。——Harold Abelson 和 Gerald Jay Sussman

避免不必要/小聪明式的重载

有一个古怪的图形界面库,用 w+c 表示在图形组件 w 上添加子控件 c,这显然是不必要的。

尽量使用命名变量,而非临时变量


第 7 条:伸缩性的考量

不要进行不成熟的优化,也不要随意将程序劣化。

寻找平衡点的关键在于:集中精力改善算法的复杂度,而非拘泥于节省一次循环,一个加法等小型优化。


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

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

现代计算机大都具有极为复杂的计算模型,经常是几个流水线处理单元并行工作,深高速缓存层次结构,猜测执行(speculative execution),分支预测……这些都还只是 CPU 层面的优化。

在诸多硬件优化之上,还有编译器的全力优化,而在这些复杂的架构之上,还有你——程序员的猜测。因此,许多的“优化”措施实际上不一定真的能发挥作用。

而且,现代的程序,它的整体性能不一定仅仅受计算性能的限制,它还受内存大小的限制,网络性能的限制,数据库连接,硬盘读写等诸多限制。仅仅在计算方面的优化,对整体性能的提升十分有限,投入宝贵的人力物力去改善这些效果并不显著的地方很不划算。

因此,应将正确性放在第一位,算法的复杂度第二,其次在考虑细枝末节的优化。

书中给出的例子是:

  • ++-- 的后缀运算的不当使用。
  • inline 的滥用。
  • 纠结于引用传参与复制传参。

第 9 条:避免不成熟的劣化(pessimization)

有些地方本可以通过简单的方式达到更好的性能,却因一时方便采取低效的方式。这是与过度优化相反的另一个极端。

书中多处提到程序劣化的例子。

善用抽象与库是解决问题的方式之一。


第 10 条:尽量减少全局和共享的数据

  • 全局变量造成了远距离数据间的耦合,这使程序的维护和调试更加困难。
  • 全局变量还常常会污染全局命名空间。
  • 全局变量的初始化顺序是未定义的,这使全局变量的初始化十分复杂。
  • 请尽量用通信方式(如消息队列)代替全局变量。

第 11 条:隐藏信息

  • 隐藏信息意味着接口的不变性,它限制了变化的范围,避免造成 “连锁反应” 。
  • 尽量不要公开类的数据成员,或其句柄与指针。一个例外是纯粹的值的集合(如 C 中的 struct),和进行白箱测试时。

第 12 条:懂得何时、如何进行并发性编程

(我还没学会并发编程,之后会补上的……)

第 13 条:确保资源为对象所拥有,使用显示的 RAII 和智能指针

  • C++ 的 “资源获取即初始化”(Resource Acquisition Is Initialization,RAII)是正确处理资源的利器。
  • 获取原始资源时,应立即将其传递给所属对象。
  • 一条语句内只分配一个对象的资源。
  • 资源的初始化应在构造函数中进行,其销毁应在析构函数中进行。
  • 实现 RAII 类时,要注意构造和赋值函数的设计,编译器生成的默认版本不一定正确,单例的资源应禁止其复制(但可以移动)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值