软件设计: 一般构想 -> implement + interface
本章阐述 良好 C++ interface 的 设计和声明
封装性 / 扩展性 / 一致性 / 高效性
1 interface
要 易使用 / 不易 误用
1 `防止 interface 误用`
4 种方法
(1) `建立 新/wrapper 类型`
防止参数次序 传错
Date(Month(3), Day(30), Year(1995) );
(2) `限制 对象有效值: 预先定义 所有有效值`
Date d(Month::Jan(), Day(30), Year(2000) );
(3) `限制 类型上的操作`
`1) const`
乘 运算符 operator* 的 return type 用 const 限制
可 防止 a*b = c 的 用户无意错误
if( a*b = c ) // 原意 是 ==, 结果不小心写错了
(4) `不要让 client 直接管理资源`
`任何接口, 若 要求 client 必须记得某些事情, 就有着 不正确使用 的 倾向`
Factory Method 返回 new 对象 的 raw pointer
R* createR();
为避免 资源泄露, 返回的 `raw pointer 最终必须被删除`
=> 至少开启 2 个错误
[1] 没 delete ptr
[2] delete ptr 超过 1 次
|
| 解决
|/
`返回 的 raw pointer 存到 smart pointer, 将 delete 的 责任 推给 SP`
|
| 更好的设计: 先发制人
|/
`令 Factory function 返回 SP -> 强迫 client 将 return value 存到 SP`
std::shared_ptr<R> createR();
2 促进 interface 正确使用
(1) `接口一致性`
C++
STL容器: 均用 size()
(2) `与 build-in 类型 行为 兼容`
3 shared_ptr 定制 deleter`
(1) RC == 0 时, 自动调用 deleter`
std::shared_ptr<R> createR()
{
std::shared_ptr<R> spR( static_cast<R*>(0), clearR );
spR = ... // 指向 目标 object
return spR;
}
2 设计 class 就像 设计 type
3 选 pass-by-reference-to-const
还是 pass-by-value
1 pass by value to/from function 的 代价
arg -> copy ctor / return value -> copy ctor + 相应 dtor
2 等价 但 更高效的做法: pass-by-reference-to-const
(1) by reference
[1] `回避 那些 ctor / dtor: 因为 没 新对象 被创建`
[2] `avoid `对象切割 slice` 问题
`Derived 对象 pass by value 到 para 为 Base 的 func -> Derived 对象 的 derived part 被 切除`
(2) const
`防止 arg 被修改`
3 pass by value / pass by reference to const 两者如何取舍 ?
————————————————————————————
选 pass by value
[1] 内置类型
[2] STL iterator
[3] function object
————————————————————————————
pass by reference to const
else
————————————————————————————
Note
而不是 根据 type 本身大小
许多 对象
包括 `大多 STL 容器` —
`内含 的 东西只比 1 个 指针` 多一些,
但 `复制这种对象` 却需承担 `复制 那些 指针 所指的 每一样东西`
4 必须 return object 时, 不要 return reference
1 `乘运算符` 正常应返回 const type
欲尝试 返回 ref
=> `函数内 应 创建 新对象`
3 种方法 及其 问题`
[1] `stack 空间 创建之`
local 变量 => 函数 return 后 被销毁
=> ref 指向 曾经的 A 但 已被销毁 的 memory
=> undefined behavior
[2] `heap 空间 创建之`
谁负责 delete ?
同一语句 调 2 次 乘: w = x * y * z; // operator*( operator*(x, y), z )
=> 2 次 new
=> `没法合理 取出` 返回的 `ref 背后 隐藏 的 指针`
=> 没法 合理 delete ptr
[3] `local static 对象`
1] not 多线程 安全
2] 深层次 瑕疵
if( (a*b) == (c*d) )
即 if( operator==( operator(a, b), operator(c, d) ) ) 永远为真`
`两次返回 的 ref` 都 指向 `内部定义的 static 对象`
client 看到的 `永远都是 static 对象` 的 `现值`
|
| 解决
|/
`必须返回 新对象 时, 就返回 新对象`
2 conclusion
——————————————————————————————————————————————
绝不要 返回
——————————————————————————————————————————
(1) ptr / ref 指向 的 local stack 对象
——————————————————————————————————————————
(2) ref 指向 的 heap 对象
——————————————————————————————————————————
(3) ptr / ref 指向 的 local static 对象,
+ `可能同时需要 多个这样的对象`
——————————————————————————————————————————————
5 mem data
声明为 private
1 mem data 为什么 不采用 public
封装
2 mem data 为什么 不采用 protected
理由 与 public 相近
从 `封装` 角度看, `只有2种 访问权限: private ( 提供 封装 ) 和 其他 ( 不提供 封装 )`
6 选 non-mem / non-friend
还是 mem func
1 non-mem func 优于 mem func` 的 2个地方`: `封装性` 更高 & `包裹弹性` 更大
| |
| | =>
|/ |/
与 直观相反 `编译依赖` 更低
(1) 封装性
non-mem no-friend func vs. mem func
————————————————————————————————————————————————————————————————————————
前提: 提供 相同机能
|
|如
|/
都能访问 public mem func
————————————————————————————————————————————————————————————————————
前者 `只能访问 指定 public mem func`
————————————————————————————————————————————————————————————————————
后者 还可访问 private data / private function / enum / typedef
————————————————————————————————————————————————————————————————————
=> 后者 封装性 更低
————————————————————————————————————————————————————————————————————————
(2) `分离 编译依赖`
|\
| 作用 2
| 作用 1
`utility func` - - - -> 提高封装性
|
| 指
|/
non-mem non-friend func
utility func 想访问 的 public mem func 所在 class
|\
|
[1] 将 `utility func` 和 `目标 class` 放 同一 utility namespace`
+
`namespace 可 跨多个源文件`
|
| Note
|/
class 不能
将 `作用不同` 的各 `utility functions`
放 `不同 头文件` 但 `隶属 同一 namespce`
可实现 `分离 编译依赖关系 => 切割机能`
`不同 clients 只需要 与 其感兴趣的
utility funcs 编译依赖
#include "some_utility_func.h"
[2] STL 正是用这种思路
<vector> <list> ... 声明 std 某机能,
若 client 只想用 vector 机能,
只需 #include <vector>
// a.h
namespace N
{
class A { ... };
// 核心机能 的 utility func
}
// a_function1.h
namespace N
{
// 与 功能1 相关 的 utility func
}
7 若 所有 参数 ( 包括 this 所指 *this: 如 运算符重载函数 的 lhs 相应实参 ) 均 需 type conversion
, 则 用 non-mem func
`分数 类 ( 分子 / 分母 )` + `乘 运算符`
|
| lhs + rhs 2 个 para 均 `转换为/取出` 分子 分母
|/
分子分母 分别相乘
————————————————————————————
`乘 运算符`
选 哪种函数 ?
————————————————————
[1] mem func
————————————————————
[2] non-mem func
————————————————————
[3] 是否 作 friend
————————————————————————————
1 用 mem func
class A
{
public:
A (int numerator = 0;,
int denominator = 1);
int numerator() const;
int denominator() const;
const A operator*(const A& rhs) const;
};
`左运算对象 为 数字字面量` 时
`不满足 交换律`
result = 2 * oneHalf; // error: 2.operator*(oneHalf);
|
| 乘 要满足 交换律
|
| 解决
|/
2 non-mem func
const A
operator*(const A& lhs,
const A& rhs)
{
return A(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator() );
}
3 是否作 friend?
答: 不必要
任何时候, 可避免 friend 就 避免
8 不抛出异常
的 swap
0 swap 应用
——————————————————————————————————
[1] `exception-safe 编程 的脊柱`
——————————————————————————————————
[2] 处理 `自我赋值`
——————————————————————————————————
swap: 置换两对象的值
1 缺省 swap
std::swap 典型实现: 引用传递 + 3 次 copy
|
| 要求
|/
T 支持 copying
template<typename T>
void std::swap(T& a, T& b) { T temp(a); a = b; b = temp; }
问题: 对 某些 类型, 3 次复制 无一必要
|
| 如
|/
用 pimp 手法 的 类型
2 `用 pimp 手法 的 类型 A` 下 针对 `T 为 A` 设计 `std::swap 全特化` 版本
(1) 缺省 swap 问题
效率低
`不止 copy 3 个 对象, 还 copy 3 个 Impl 对象`
A& A::operator=(const A& rhs){
// ...
*pImpl = *(rhs.pImpl);
// ...
}
|
| 解决
|/
思路
`swap 2 个 对象, 只需 swap 其 内部 pImpl 指针`
`std::swap 全特化: 目标 类型 T == A`
template<>
void std::swap<A> (A& a, A& b) { swap(a.pImpl, b.pImpl); }
| 问题
|/
A 对象 直接访问 private 成员 => 编译不过
|
| 解决
|/
std::swap `全特化 版`
|
| template<>
| void std::swap<A>(A& a, A& b)
|/
调 `A 成员函数 swap`
|
| swap(pImpl, rhs.pImpl);
|
|/
调 标准 swap: internal ptr 作 实参
与 STL 容器 做法相同
3 泛化: A / AImpl 均为 class template
(1) std 内 偏特化 template func -> 编译报错
C++ `只 允许 偏特化 template class`
C++ `不 允许 偏特化 template func`
template<typename T>
void std::swap< A<T> >(A<T>& a, A<T>& b)
|
| 解决
|/
(2) `添加 templete func 重载`
std 内 添加 template func 重载 -> 编译报错
std 内 `不可 添加 新的 template, 可 全特化 template`
template<typename T>
void std::swap(A<T>& a, A<T>& b)
| 解决
|/
(3) `自定义 namespace` 内 添加 template func 重载 ( swap ) -> 调 `A::swap`
这种做法 对 class / class template 都 ok
———————————————————————————————————————————————————————————————
(4) `client 调 swap 匹配顺序`
———————————————————————————————————————————————————————————
[1] ADL
`T 所在 namespace 之 T 专属 swap
|
| 如: 调
|/
T 内 成员函数 swap
|
| 调
|/
internal ptr 作 实参 调
经 using 声明 引入的
标准 swap // std::swap
———————————————————————————————————————————————————————————
[2] std::swap 之 全特化版
———————————————————————————————————————————————————————————
[3] std::swap 之 经典版
———————————————————————————————————————————————————————————————
template<typename T>
void f(T& obj1, T& obj2)
{
// [1] 令 std::swap 在 该 scope 内 可用
using std::swap;
// [2] ADL: 不必 using T_namespace::swap
swap(obj1, obj2); // 如 T == A 或 N::A<double>
} |
|/
template<typename T>
void N::swap(A<T>& a, A<T>& b) { a.swap(b); }
|
|/
template<typename T>
N::A::swap(pImpl, rhs.pImpl);
|
|/
using std::swap;
————————————————————
test
————————————————————
T 为 3 种 类型
————————————————
[1] 内置类型
[2] 类
[3] 类模板
————————————————————
4 mem swap 绝不可抛出异常
`swap 的 最好应用: 提供 `exception-safety`
`自定义 swap` 版本 往往同时提供
`高效 swap` 和 exception-safety`
|
| 其上 操作 绝不会 抛出异常
| |\
| |
|/ |
`高效 swap` 几乎总是 基于 `内置操作`
|
| 如
|/
pImpl 手法 的 internal ptr
=== 详细
1 interface
要 易使用 / 不易 误用
0 概述
(1) C++ 的 各种 interface
function 接口
class 接口
template 接口
接口: client 与 code 交互入口
(2) 若 client 企图 使用某个接口, 却 没有获得预期结果,
该 code 就不该 编译通过 -> 责任就在于 coder
1 `防止 interface 误用`
4 种方法
(1) `建立 新/wrapper 类型`
防止参数次序 传错
Date(Month(3), Day(30), Year(1995) );
class Date
{
public:
Date(int month, int day, int year);
};
class Date
{
public:
Date(const Month& m, const Day& d, const Year& y);
};
(2) `限制 对象有效值: 预先定义 所有有效值`
Date d(Month::Jan(), Day(30), Year(2000) );
class Month
{
public:
static Month Jan() { return Month(1); }
// ...
static Month Dec() { return Month(12); }
private:
explicit Month(int m);
};
(3) `限制 类型上的操作`
`1) const`
乘 运算符 operator* 的 return type 用 const 限制
可 防止 a*b = c 的 用户无意错误
if( a*b = c ) // 原意 是 ==, 结果不小心写错了
`2) 限制 自定义 type 与 内置类型` 的 `同一操作 表现一致`
int 型 *
不允许 a*b = c 的 操作
(4) `不要让 client 直接管理资源`
2 促进 interface 正确使用
(1) `接口一致性`
C++
STL容器: 均用 size()
不一致性 对 开发人员造成 心理和精神折磨
Java
array / string: length()
list: size()
(2) `与 build-in 类型 行为 兼容`
3 `shared_ptr 定制 deleter`
(1) RC == 0 时, 自动调用 deleter
(2) std::shared_ptr `缺省 deleter 来自 源 DLL` -> 防范 `跨 DLL 问题: object 在 某一 DLL(动态链接库) 被 new, 却在 另一 DLL 中 被 delete`
4 Conclusion
shared_ptr 让 接口设计者 阻止了 大群 client 犯下 资源泄露 的 错误
2 设计 class 就像 设计 type
1 object `如何 创建 & 销毁`
ctor
dtor
// 打算定制 内存分配和释放 的话
operator new/delete []
2 object `初始化 / 赋值` 差别
3 object 若被 pass by value, 意味着 什么?`
copy ctor 定义 type 的 pass by value 如何实现
4 `有效值`
决定
invariant ( 约束条件 )
ctor / assignment / setter 函数 必须进行的 错误检查
5 是否需要 配合某 继承体系
6 需怎样的 conversion
(1) T1 -> T2 隐式转换
1) T1 内: 类型转换运算符 T1::operator T2 ()
2) T2 内: non-explicict-one-argument Ctor
(2) T1 -> T2 显式转换
T1::get() 成员函数
7 合理的 操作符 和 函数
声明 哪些 function
哪些 该为 member func
8 哪些 compiler 隐含自动生成的 函数 该弃用
声明为 private
9 谁该 取用 新 type 成员
mem 该放 public / private / protected ?
哪个 class / function 该为 friend
10 type 家族 -> class template
11 是否真的需要 新 type?
也许 non-member function 或 template 就能达到目标
3 选 pass-by-reference-to-const
还是 pass-by-value
1 pass by value to/from function 的 代价
arg -> copy ctor / return value -> copy ctor + 相应 dtor
Base / Derived 都只有 1 个 std::string 成员数据
Derived 以 by value 方式 传给 函数 时, 发生什么?
Base / Derived copy ctor
+ 相应的 string mem 的 copy ctor
+ 相应 dtor
= 4 次 ctor + 4 次 dtor
2 等价 但 更高效的做法: pass-by-reference-to-const
(1) by reference
[1] `回避 那些 ctor / dtor: 因为 没 新对象 被创建`
[2] `avoid `对象切割 slice` 问题
`Derived 对象 pass by value 到 para 为 Base 的 func -> Derived 对象 的 derived part 被 切除`
class Base
{
public:
virtual void vf() const;
};
class Derived: public Base
{
public:
virtual void vf() const;
};
void f(Base b) { b.vf(); } // Derived 对象 被 切割
Derived d;
f(d); // f 内 调 Base::vf()
|
| 解决
|/
void f(const Base& rb) { rb.vf(); }
(2) const
`防止 arg 被修改`
3 pass by value / pass by reference to const 两者如何取舍 ?
4 必须 return object 时, 不要 return reference
1 乘运算符 正常应返回 const type
欲尝试 返回 ref
=> `函数内 应 创建 新对象`
3 种方法 及其 问题`
[1] `stack 空间 创建之`
local 变量 => 函数 return 后 被销毁
=> ref 指向 曾经的 A 但 已被销毁 的 memory
=> undefined behavior
const A&
operator* (const A& lhs, const A& rhs)
{
A ret(...);
return ret;
}
[2] `heap 空间 创建之`
谁负责 delete ?
同一语句 调 2 次 乘
=> 2 次 new
=> `没法合理 取出` 返回的 `ref 背后 隐藏 的 指针`
=> 没法 合理 delete ptr
const A&
operator* (const A& lhs, const A& rhs)
{
A* ret = new A(...);
return *ret;
}
A w, x, y, z;
w = x * y * z; // operator*( operator*(x, y), z )
[3] `local static 对象`
1] not 多线程 安全
2] 深层次 瑕疵
if( (a*b) == (c*d) )
即 if( operator==( operator(a, b), operator(c, d) ) ) 永远为真`
`两次返回 的 ref` 都 指向 `内部定义的 static 对象`
client 看到的 `永远都是 static 对象` 的 `现值`
|
| 解决
|/
`必须返回 新对象 时, 就返回 新对象`
const A
operator* ( const A& lhs,
const A& rhs)
{
return A(...);
}
2 conclusion
——————————————————————————————————————————————
绝不要 返回
——————————————————————————————————————————
(1) ptr / ref 指向 的 local stack 对象
——————————————————————————————————————————
(2) ref 指向 的 heap 对象
——————————————————————————————————————————
(3) ptr / ref 指向 的 local static 对象,
+ `可能同时需要 多个这样的对象`
——————————————————————————————————————————————
5 mem data
声明为 private
1 mem data 为什么 不采用 public
(1) 语法 `一致性`
public 接口 内 都是 function, client 访问 mem data 时, 就不必记住是否要用 圆括号了, 都用就是了
(2) 封装
mem data 放 public -> 谁都可以 直接访问之
`将 mem data 隐藏 在 函数 interface 背后`
可为 `所有可能的实现` 提供弹性
1) 实现出
`不准访问 / 只读 / 只写 / 读写`
2) `mem data 被 读/写 时, 可 notify 其他对象`
3) 验证 class 约束条件
4) `多线程 中 同步`
`5) 日后可以 改为 某个计算 替换( 省掉 mem data 所占内存 / 速度慢 )` 该 mem data,
而 client 不会知道内部已变化
这般能力 等价于 其他语言中的 `properties`
// 不准访问/只读/只写/读写
class AccessLevel
{
public:
int getReadOnly() const { return readOnly; }
void setWriteOnly(int value) { writeOnly = value; }
int getReadWrite() const { return readWrite; }
void setReadWrite(int value) { readWrite = value; }
private:
int noAccess;
int readOnly;
int writeOnly;
int readWrite;
};
一直计算并更新 平均速度 放 mem data, 调用时直接读 mem data 值
vs. 每次调用时, 才计算平均速度 并 返回
mem data 占内存, 但 调用时 速度快
vs. 不多占内存, 但 调用时 速度慢
2 mem data 为什么 不采用 protected
理由 与 public 相近
从 `封装` 角度看, `只有2种 访问权限: private ( 提供 封装 ) 和 其他 ( 不提供 封装 )`
6 选 non-mem / non-friend
还是 mem func
1 non-mem func 优于 mem func` 的 2个地方`: 封装性 更高 & 包裹弹性 更大`
| |
| | =>
|/ |/
与 直观相反 编译依赖 更低
(1) `与 直观相反, non-mem func 封装性 高于` mem func
OO 要求 data 尽可能 被 封装
OO 建议 `data 和 操作 data 的 函数 应 捆绑在一起`
=> 貌似 mem func 比 non-mem func 好, 但 该建议 `并不正确`
(2) `包裹弹性 更大 => 编译依赖 更低`
class WebBrowser
{
public:
void clearCache(); // 下载高速缓存
void clearHistory(); // 访问过的 URLs 历史记录
void clearCookies(); // cookies
};
// 1 整个操作, 执行 3 个 动作
// (1) mem func
class WebBrowser
{
public:
void clearEverything(); // 调 前3者
};
// (2) non-mem non-friend func
void clearBrowser(WebBrowser& wb)
{
wb.clearCache();
wb.clearHistory();
wb.clearCookies();
}
2 数据 `封装性`
`越少 code 能 访问 数据, 数据 封装性越高
改变数据 对 clients 影响越少`
以 `能访问数据的 函数个数` 衡量 `数据封装性:`
越少 函数 能访问 -> 封装性 越高
3 `utility func` 提高封装性 + `分离编译依赖`、
7 若 所有 参数 ( 包括 this 所指 *this: 如 运算符重载函数 的 lhs 相应实参 ) 均 需 type conversion
, 则 用 non-mem func
8 不抛出异常
的 swap
0 swap 应用
——————————————————————————————————
[1] `exception-safe 编程 的脊柱`
——————————————————————————————————
[2] 处理 `自我赋值`
——————————————————————————————————
swap: 置换两对象的值
1 缺省 swap
std::swap 典型实现: 引用传递 + 3 次 copy
|
| 要求
|/
T 支持 copying
namespace std
{
template<typename T>
void swap(T& a, T& b)
{
T temp(a);
a = b;
b = temp;
}
}
问题: 对 某些 类型, 3 次复制 无一必要
|
| 如
|/
用 pimp 手法 的 类型
2 `用 pimp 手法 的 类型 A` 下 针对 `T 为 A` 设计 `std::swap 全特化` 版本
(1) 缺省 swap 问题
效率低
`不止 copy 3 个 对象, 还 copy 3 个 Impl 对象`
class AImpl // 针对 A 设计的 class
{
private:
int a, b, c;
std::vector<double> vec; // 意味着 copy 时间很长
};
class A
{
public:
A(const A& rhs);
// 复制 A 时, 还复制 其 AImpl 对象
A& operator=(const A& rhs)
{
// ...
*pImpl = *(rhs.pImpl);
// ...
}
private:
AImpl* pImpl; // pImpl 指针, 所指对象 内含 A 数据
};
|
| 解决
|/
思路
`swap 2 个 对象, 只需 swap 其 内部 pImpl 指针`
`std::swap 全特化: 目标 类型 T == A`
通常, `不允许 改变` std namespace 内 任何东西,
但 可以 为 标准 template ( 如 swap ) 建 特化版本,
使之专属于 自建 class
namespace std
{
template<>
void swap<A> (A& a, A& b)
{
swap(a.pImpl, b.pImpl);
}
}
|
| 问题
|/
A 对象 直接访问 private 成员 => 编译不过
|
| 解决
|/
std::swap `全特化 版` -> 调 `A 成员函数 swap` -> 调 标准 swap: internal ptr 作 实参
与 STL 容器 做法相同
class A
{
public:
void swap(A& rhs)
{
using std::swap;
/|
/ 对 internal ptr 调 标准 swap
/
swap(pImpl, rhs.pImpl); // class 内 本 class 任何对象 可直接访问其 private mem
} |\
|
}; - - - - - - - -
|
namespace std |
{ |
// 全特化: 对 A |
template<> |
void swap<A>(A& a, A& b)|
{ |
a.swap(b); - - - - -
}
};
3 泛化: A / AImpl 均为 class template
template<typename T>
class AImpl{ ... };
template<typename T>
class A{ ... };
(1) std 内 偏特化 template func -> 编译报错
C++ `只 允许 偏特化 template class`
C++ `不 允许 偏特化 template func`
namespace std
{
template<typename T>
void swap< A<T> >(A<T>& a, A<T>& b)
{
a.swap(b);
}
};
|
| 解决
|/
(2) `添加 templete func 重载`
std 内 添加 template func 重载 -> 编译报错
std 内 `不可 添加 新的 template, 可 全特化 template`
namespace std
{
template<typename T>
void swap(A<T>& a,
A<T>& b)
{
a.swap(b);
}
};
|
| 解决
|/
(3) `自定义 namespace` 内 添加 template func 重载 ( swap ) -> 调 `A::swap`
这种做法 对 class / class template 都 ok
———————————————————————————————————————————————————————————————
(4) `client 调 swap 匹配顺序`
———————————————————————————————————————————————————————————
[1] ADL
`T 所在 namespace 之 T 专属 swap
|
| 如: 调
|/
T 内 成员函数 swap
|
| 调
|/
internal ptr 作 实参 调
经 using 声明 引入的
标准 swap // std::swap
———————————————————————————————————————————————————————————
[2] std::swap 之 全特化版
———————————————————————————————————————————————————————————
[3] std::swap 之 经典版
———————————————————————————————————————————————————————————————
namespace N
{
template<typename T>
class AImpl
{
private:
int a, b, c;
std::vector<T> vec;
};
template<typename T>
class A
{
public:
template <typename T>
void swap(A<T>& rhs)
{
using std::swap;
/|
/
swap(pImpl, rhs.pImpl);
} |\
|
- - - - - - -
private: |
AImpl<T>* pImpl; |
}; |
|
template<typename T> |
void swap(A<T>& a, |
A<T>& b) |
{ |
a.swap(b); - - - - -
} |\
} |
- - - - - - - -
|
| template<typename T>
| void f(T& obj1, T& obj2)
| {
| // [1] 令 std::swap 在 该 scope 内 可用
| using std::swap;
|
| // [2] ADL: 不必 using T_namespace::swap
|_ _ _ _swap(obj1, obj2); // 如 T == A 或 N::A<double>
}
————————————————————
test
————————————————————
T 为 3 种 类型
————————————————
[1] 内置类型
[2] 类
[3] 类模板
————————————————————
#include <vector>
//--- 1 class
class AImpl_2
{
private:
int a, b, c;
std::vector<double> vec;
};
class A_2
{
public:
void swap(A_2& rhs)
{
using std::swap;
swap(pImpl, rhs.pImpl);
}
private:
AImpl_2* pImpl;
};
//(3) std::swap 全特化版
namespace std
{
template<>
void swap(A_2& a, A_2& b) { a.swap(b); }
};
//--- 2 class template
namespace N
{
template<typename T>
class AImpl
{
private:
int a, b, c;
std::vector<T> vec;
};
template<typename T>
class A
{
public:
template <typename T>
void swap(A<T>& rhs)
{
using std::swap;
swap(pImpl, rhs.pImpl);
}
private:
AImpl<T>* pImpl;
};
//(2) T 所在 namespace 之 T 专属 swap
template<typename T>
void swap(A<T>& a,
A<T>& b)
{
a.swap(b);
}
}
template<typename T>
void f(T& obj1, T& obj2)
{
using std::swap; // 令 std::swap 在 此函数内 可用
// ADL
swap(obj1, obj2);
}
int main()
{
int a = 0;
int b = 1;
N::A<double> w1;
N::A<double> w2;
A_2 w3;
A_2 w4;
// (1)
f(a, b);
// (2)
f(w1, w2);
// (3)
f(w3, w4);
}
4 mem swap 绝不可抛出异常
`swap 的 最好应用: 提供 `exception-safety`
`自定义 swap` 版本 往往同时提供
`高效 swap` 和 exception-safety`
|
| 其上 操作 绝不会 抛出异常
| |\
| |
|/ |
`高效 swap` 几乎总是 基于 `内置操作`
|
| 如
|/
pImpl 手法 的 internal ptr
网友评论