本篇笔记包含 Term 18~25。
Term 18:让接口容易被正确使用,不易被误用
这一点其实比较难做到,可以参考这几个方面:
- 接口的一致性,与内置类型(包括 STL)的行为兼容
- 防止误用包括建立新类型、限制类型上的操作、束缚对象的值以及自动管理资源
- shared_ptr 支持定制型 deleter,可以防范 DLL 问题,可用来自动解除互斥锁
Term 19:设计 class 犹如设计 type
在 C++ 中,设计一个通用的 class 就是设计一个新的类型,需要考虑以下一些方面:
- 新 type 的对象应该如何被创建和销毁?
- 对象的初始化和对象的赋值该有什么样的差别?调用不同的函数
- 新 type 的对象如果被 passed by value,意味着什么?会调用 copy 构造函数
- 什么是新 type 的合法值?
- 你的新 type 需要配合某个继承图系吗?继承自某个 class 或者允许被继承有不同的设计要点
- 你的新 type 需要什么样的转换?考虑是否需要在不同的 class 对象之间转换,设计显式/隐式转换
- 什么样的操作符和函数对此新 type 而言是合理的?
- 什么样的标准函数应该驳回?禁用编译器自动生成的函数
- 谁该取用新 type 的成员?考虑如何设置 public/protect/private 成员以及 friends
- 什么是新 type 的 undeclared interface?
- 你的新 type 有多么一般化?考虑定义一个新的 class template
- 你真的需要一个新 type 吗?也许通过多个函数或者 template 就能达到需要
Term 20:宁以 pass-by-reference-const 替换 pass-by-value
C++ 默认以 value 形式进行传递,这会带来大量的复制消耗,而且可能造成对象切割(slicing)问题。以 reference 传递会高效很多。但也有例外,内置类型、STL 迭代器以及函数对象以 value 传递更加合适。
Term 21:必须返回对象时,别妄想返回器 reference
返回 pointer/reference 指向 local 对象是错误行为,指向 local static 也是愚蠢的行为(但可行),reference 指向 heap-allocated 对象会内存泄漏。
Term 22:将成员变量声明为 private
简单来说,就是为了提高 class 的封装性
Term 23:宁以 non-menber、non-friend 替换 member 函数
这样既可以增加 class 的封装性,也可以让函数更容易扩充。
Term 24:若所有参数皆需类型转换,请为此采用 non-menber 函数
采用 non-menber 函数可以进行合适的隐式类型转换。
Term 25:考虑写出一个不抛出异常的 swap 函数
STL 中默认的 swap 函数会调用 copy 构造函数和 copy 赋值运算符,在某些情况下带来不必要的性能损耗。可以考虑的改写思路:
- 添加一个用于 swap 的 public member 函数(不能抛出异常)
- 在 class 或者 template 的命名空间提供一个 non-member 函数 swap,用这个函数调用 1. 中的 member 函数 swap。这点可适用于 class template
- 特化 std::swap 来调用 member 函数 swap
- 用户使用 swap 时应当使用
using std::swap;
template<typename T>
void doSomething(T& obj1, T& obj2){
using std::swap; // 让 std::swap 在此函数内可见
swap(obj1, obj2); // 自动选择最佳的 swap 版本
}