本篇笔记包含 Term 07~17。

Term 07:在创建对象时注意区分 () 和 {}

在 Term 2 中,我们了解到 {val...} 实际是 std::initializer_list<T> 类型,使用 {} 初始化需要注意:

  1. 阻止隐式窄化型别转换
  2. Foo f{}; 会调用 class Foo 的无参构造函数
  3. 如果一个类实现了 std::initializer_list<T> 为参数的构造函数,则除了 2. 之外,所有使用 {} 的情况都会最优先调用这个构造函数,因此除了明确调用 std::initializer_list<T> 为参数的构造函数的时候,其它情况都应该使用 ()
  4. Foo f({}); 或者 Foo f{{}}; 会调用 std::initializer_list<T> 为参数的构造函数,实参为空的 std::initializer_list<T>
  5. 非静态成员变量不能使用 (),不可复制对象不能使用 = 操作符

Term 08:优先选用 nullptr,而非 0 或者 NULL

使用 nullptr 表示空指针(任意类型指针),可以避免 0 或 NULL 错误调用 int 类型的重载

Term 09:优先选用别名声明,而非 typedef

使用别名声明,在函数指针上有优势,并且别名声明支持模板化,而 typedef 不支持。可以通过构造一个模板结构体/类,内嵌 typedef 声明变量来达到类似效果,但是比较繁琐。

// using 写法
template<typename T>
using MyAllocList = std::list<T, MyAlloc<T>>;
MyAllocList<Widget> lw;
// typedef 写法
template<typename T>
struct MyAllocList {
    typedef std::list<T, MyAlloc<T>> type;
}
MyAllocList<Widget>::type lw;

Term 10:优先选用限定作用域的枚举型别,而非不限作用域的枚举型别

不限作用域的枚举型别,变量作用域是在外层,造成污染,使用限定作用域的枚举型别(enum class)可以避免这一情况。限定作用域的枚举型别需要强制类型转换,默认的底层类型是 int,可在声明式指定类型。

Term 11:优先选用删除函数,而非 private 未定义函数

当我们需要禁止编译器编写某个默认的类成员函数时(例如 copy 构造函数),我们可以使用 Foo(const Foo&) = delete 告知编译器禁止生成该成员函数。除了成员函数之外,我们还可以声明其它函数为删除函数,用以避免隐式转换或者时处理特殊边界情况。

Term 12:为意在改写的函数添加 override 声明

注意 overload 和 override 的区别 ,当我们意图 override 一个虚函数时,应该使用 override 让编译器检查这是否是合法的 override 行为。

Term 13:优先选用 const_iterator,而非 iterator

  1. 优先选用 const_iterator,而非 iterator
  2. 优先选用非成员函数版本的 begin/cbegin 等,而非成员函数版本

Term 14:只要函数不会发射异常,就为其加上 noexcept 声明

添加 noexcept 声明使得编译器能更好地优化。

Term 15:只要有可能使用 constexpr,就使用它

  1. constexpr 对象具备 const 属性,并切在编译期就已经知道其值
  2. 如果 constexpr 函数传入地实参是编译期已知的值,那么其结果会在编译期计算得出,否则退化为普通函数

Term 16:保证 const 成员函数的线程安全

通常来说,只读属性是线程安全的。const 成员函数可以保证对象本身不变(除了 mutable 属性),但是 mutable 属性可能造成在读取成员属性时线程不安全(比如实现了缓存)。我们需要编写线程安全的代码避免这种情况。

Term 17:理解特种成员函数的生成机制

  1. 当我们没有为类编写特种成员函数,而代码中使用到了,编译器会自动替我们生成。他们包括:默认构造函数、析构函数、复制构造函数、复制赋值运算符、移动构造函数和移动赋值运算符
  2. 移动操作仅当类中没有显式声明复制操作、析构函数、移动操作时才会自动生成
  3. 复制构造函数、复制赋值运算符和析构函数,三者应该同时自定义或者同时都不自定义
  4. 使用 =delete=default 声明特种成员函数是使代码更加明确的做法
  5. 成员函数模板不会抑制特种成员函数的生成