全特化版本,与 STL 保持一致性,这样 Widget 对象就可以正常的调用 swap 了
`cpp class Widget { public:
void swap(Widget& other) {
using std::swap;
swap(pImpl, other.pImpl);
} private:
WidgetImpl *pImpl; }; namespace std { // std
template <>
void swap<Widget> (Widget& a, Widget& b) { a.swap(b); } }
偏特化一个 function template 时,惯用手法是简单的为它添加一个重载版本
cpp namespace WidgetFpp { // 这里不是添加到 std 中了
class WidgetImpl {};
class Widget {};
template<typename T>
void swap(Widget<T>& a, Widget<T>& b) { a.swap(b); } }
一般经验
能使用 std::swap 就不去造轮子
有 pimpl 手法的,或类似的
提供一个 public swap,置换该类型的两个对象
在当前 class 或者 template 所在的命名空间内提供一个 non-member swap,并调用上述成员函数
如果当前是 class 而不是 class template,为class特化 std::swap,并调用 swap 成员函数
在调用 swap 前,使用 using std::swap, 自动匹配合适的那个 swap 函数 实现
Item 26 - 尽可能延后变量定义是的出现时间
以“具明显意义之初值”将变量初始化,还可以附带说明变量的目的
Item 27 - 尽量少做转型动作
书中对 Window::OnResize() 的分析,这里用过代码看结果,L18 这里转型后调用的OnResize()的*this是一个副本,L19 直接调用基类 OnResize能够得到正确的结果
cpp 7 class Window { 8 public: 9
virtual void OnResize() { 10
a = 3; 11
}
12
int a; 13 };
14 15 class SpWindow : public Window { 16 public: 17
void OnResize() override { // C++ 11 18
// static_cast<Window>(*this).OnResize(); // 这里 a = 0 19
Window::OnResize(); // a = 3 20
}
21 };
谨慎使用 dynamic_cast
Item 28 - 避免返回handles指向对象内部成分
书中的例子会造成 bitwise constness, 见 Item 3, 但是 clang 3.8 上这些代码已经不能编译通过了
Item 29 - 值得为“异常安全”花费精力
异常安全的要求
不泄露任何资源
不允许数据败坏
资源泄漏用对象管理,如锁
单个函数的安全保证,copy-and-swap
多个函数“连带效应”, 对每一个函数都实现安全保证
Item 30 - 了解 inline
inline 和 template 通常被定义于头文件内。 是因为通常而言,inlining 和 template 是编译期的行为,编译器必须知道被调用函数的本体
改变程序需要重新编译,而不能像普通的函数直接链接即可
Item 31 - 将文件间的编译关系降至最低
Handle class 和 Interface class 解除接口和实现之间的耦合关系,降低文件间的编译依赖
Item 32 - 避免遮掩继承而来的名称
derived class 继承了 base class 内的所有东西,实际上的运行方式是 derived class 作用域被嵌套在 base class 作用域内 继承与面向对象设计
Item 33 - 确认 public 继承塑造出 is-a 关系
public 继承适用于 base class 对象上的每件事情也能够作用于 derived class 对象上
base class 和 derived class 中有相同的函数名称时,base class 中的函数就被覆盖了,virtual 函数也是这样,但是有运行时多态,但是普通函数当想继承的时候就出问题了
在 derived class 加入 using base::func; 后相同参数和返回还是会覆盖 base class 中的对象
Item 34 - 区分接口继承和实现继承
C++ 的隐式规则太多,需要继承的东西干脆显示化,减少出错
Item 35 - 考虑 virtual 函数以及以外的选择
non-virtual interface, NVI 手法也是将真正需要的改变的部分分离出来放进一个函数内,而虚函数内先处理前置条件,再调用该函数
```cpp
7 class Game {
8 public:
9 int health() const {
10 std::cout << "Game::health()\n";
11 int ret = doHealth();
12 std::cout << "Game:: ret " << ret << '\n';
13 return ret;
14 }
15 private:
16 virtual int doHealth() const {
17 std::cout << "Game:: doHealth()\n";
18 return 1;
19 }
20 };
21
22 class LOL : public Game {
23 private:
24 virtual int doHealth() const {
25 std::cout << "LOL doHealth()\n";
26 return 2;
27 }
28 };
29
30 int main() {
31 LOL lol;
32 lol.health();
33 }
// 可以使用到 base class 默认的析构函数,参考 Item39,用 private 作为一种实现方式。
// LOL 未定义虚函数的时候,结果为
// Game::health()
// Game:: doHealth()
// Game:: ret 1
// Game::health()
// LOL doHealth()
// Game:: ret 2
```