「笔记」《Effective C++》 读书笔记(一)

注意!

这只是我的读书笔记,帮助我记录。其中可能有一些不严谨的解释或没有做很详细的解释,大家学习还是建议直接看书(虽然书中也有不严谨的解释)。

让自己习惯C++

条款01:视 C++ 为语言联邦

C++ 是一个多重范型编程语言,一个同时支持过程形式、对象形式、函数形式、泛型编程、元编程形式的语言。 将 C++ 的理解为一个由相关语言组成的联邦而非单一语言。记住以下四个次语言你就会发现C++p容易理解很多。 C++ 主要的四种次语言:

  • C:C++以C为基础,基本的语句、基础数据类型、预处理器等等都来自于 C,许多时候 C++ 对问题的解法其实不过就是高级的C语言解法。
  • Objective-Oriented C++:C++ 诞生之初就是想在 C 语言高效的基础上,增加 Simula 语言的特性。Class、封装、继承、多态、virtual 函数等等,改善了 C 的代码可读性、并发性、编译错误的提示等等。
  • Template C++:泛型编程,大多数程序员经验最少的部分。它带来的崭新的编程范型,所谓的模板元编程。TMP 相关规则很少与 C++ 主流编程互相影响。
  • STL:STL 是个 Template 程序库。它对容器、迭代器、算法、函数对象的规约有极佳的紧密配合与协调。STL有自己特殊的办事方式,当你同 STL 一起工作,你必须遵守它的规约。
  • C++高效编程守则视状况而变化,取决于你使用C++的哪一部分。

条款02:尽量以 const、enum、inline 替换 #define

降低对编译预处理的使用。 #define 的缺点:

  • 让编译器的错误提示不易定位
  • 缺乏类型检查
  • 不重视作用域,除非在某处 #undef,不能用来定义class专属常量,不能提供任何封装
  • 未记入符号表,增大目标代码文件

旧式的编译器不支持“in-class 初值设定”,可以用枚举来替换常量。 相比 const,enum 更像 #define,可以对 const 常量取地址,而不可以对 enum 的地址。如果不想用指针和引用值指向你的某个常量,enum 可以帮助你实现这个约束。 对于宏函数,可读性差、写宏函数时需要小心注意带上很多括号,一不小心就会出现难以检查的错误。我们可以使用 inline 关键词代替宏函数,提高代码可读性和安全性。

  • 对于单纯常量,最好以 const 对象或 enums 代替 #defines。
  • 对于形似函数的宏,最好改用 inline 函数替换 #defines。

条款03:尽可能使用 const

令函数的返回值为 const ,往往可以降低因用户错误而造成的意外,而又不至于放弃安全性和高效性。 两个成员函数如果只是常量性不同可以被重载,传入函数参数是 const 和非 const 时要区分处理,如在重载运算符 [] 时,非 const 要返回其单元的引用,const 时要返回其单元的 const 引用。 成员函数是 const 意味着它在某种程度上是不可以修改对象中的任何成员变量,但对于成员变量是指针时,它不能保证该成员函数不修改指针成员指向位置的数据。 可以使用 mutable(可变的)释放掉非 staic 成员变量的 bitwise constness 约束。 为 const 重载的函数,会造成代码复制的情况。可以通过单独写一个私有函数运行不冲突的部分,只重载与 const 相关的部分。也可以通过在 non-const里用 const_cast 去除 const 属性再返回的方式减少代码复制。但不要用在 const 函数中调 non-const 函数再返回的方式实现,这样会违背你在 const 版本中绝不改变其对象的逻辑状态的承诺,因为 non-const 版本没有做过此承诺。

  • 将某些东西声明为 const 可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数主体。
  • 编译器强制实施 bitwise constness ,但你变形程序时应该使用“概念上的常量性”(conceptual constness)。
  • 当 const 和 non-const 成员函数有着实质等价的实现时,令 non-const 版本调用 const 版本可避免代码重复。

条款04:确定对象被使用前已先被初始化

不要混淆赋值和初始化,一定要在初始化列表中初始化成员。在我之前的博文 C++类与对象总结(二)构造函数 中对构造函数中的赋值和初始化问题有一定的解释。 继承关系的初始化顺序:基类(base classes)更早于其派生类(derived classes)被初始化。 对于某个编译单元内的某个 non-local static 对象的初始化动作使用了另一个编译单元内的某个 non-local static 对象,它所用到的这个对象可能尚未被初始化,因为 C++ 对“定义于不同编译单元内的 non-local static 对象” 的初始化次序并无明确定义。可以将有初始化依赖的几个 non-local static 对象放在一个编译单元解决。 书中的解决方案为:将每个 non-local static 对象搬到自己的专属函数内(该对象在此函数内被声明为 static )。这些函数返回一个 reference 指向它所含的对象。我们将这些函数称为 reference-returning 函数。

  • 为内置型对象进行手工初始化,因为 C++ 不保证初始化它们。
  • 构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在 class 中的声明次序相同。
  • 为免除“跨编译单元之初始化次序”问题,请以 local staic 对象替换 non-local static 对象。

本文标题:「笔记」《Effective C++》 读书笔记(一)

文章作者:赵砚潇

发布时间:2018年03月16日 - 23:03

最后更新:2018年12月21日 - 20:12

原始链接:https://blog.zyx.sh/2018/03/16/effective-cpp01/

许可协议: 署名-非商业性使用-相同方式分享 4.0 国际 转载请保留原文链接及作者。

Donate comment here