1. 当 std: : swap 对你的类型效率不高时,提供一个 swap 成员函数,并确定这个函数不抛出异常。
2. 如果你提供一个 member swap,也该提供一个 non-member swap 用来调用前者。对于 classes(而非templates),也请特化 std: :swap。
3. 调用 swap 时应针对 std: :swap 使用 using 声明式,然后调用 swap 并且不带任何“命名空间资格修饰”。
4. 为“用户定义类型”进行 std templates 全特化是好的,但千万不要尝试在 std 内加入某些对 std 而言全新的东西。
swap 原本只是 STL 的一部分,但后来成为异常安全性编程(见条款29)的重要一部分,以及用来处理自我赋值可能性(见条款11)的一个常见机制。
swap 的功能是将两个对象的值赋值给彼此。缺省情况下 swap 动作可由标准库提供的swap 算法完成。swap 的典型实现如下:
namespace std {
template<typename T>
void swap(T& a, T& b)
{
T temp(a);
a = b;
b = temp;
}
}
只要类型 T 支持 copying(通过 copy 构造函数和 copy assignment 操作符完成),缺省的 swap 实现代码就会帮你置换类型为 T 的对象,你不需要为此另外再做任何工作。
swap 函数存在的问题
在“pimpl”手法中(pimpl 是“pointer to implementation”的缩写,见条款31),设计一个类 Widget:
class WidgetImpl { // 针对Widget数据设计的class
public:
...
private:
int a, b, c; // 可能有很多数据,意味着复制需要时间很长
std::vector<double> v;
...
};
class Widget { // 这个类使用pimpl手法
public:
Widget(const Widget& rhs);
// 复制Widget时,令它复制其WidgetImpl对象
Widget& operator=(const Widget& rhs)
{
...
*pImpl = *(rhs.pImpl);
...
}
...
private:
WidgetImpl* pImpl; // 指针,所指对象内含Widget数据
};
一旦要置换两个 Widget 对象,我们唯一需要做的就是置换其 pImpl 指针,但缺省的 swap 算法不知道这一点。它不止复制3个 Widgets,还复制3个 WidgetImpl 对象,效率很低下!
正确的做法
class Widget { // 与前面相同,唯一区别是增加swap函数
public:
...
void swap(Widget& other)
{
using std::swap; // 这个声明必须要,稍后解释
swap(pImpl, other.pImpl); // 若要置换Widgets就置换其pImpl指针
}
...
};
namespace std {
template<> // std::swap特化版本
void swap<Widget>(Widget&a, Widget&b)
{
a.swap(b); // 若要置换Widgets,调用其swap成员函数
}
}
上述代码中,“template<>”表示它是 std::swap 的一个全特化版本,函数名称之后的“< Widget>”表示这一特化版本是针对“T 是 Widget”而设计的。换句话说当一般性的 swap template 施行于 widgets 身上便会启用这个版本。通常我们不被允许改变 std 命名空间内的任何东西,但被允许为标准 templates(如 swap)制造特化版本,使它专属于我们自己的 classes(例如widget)。上述代码正是如此。
然而假设 Widget 和 WidgetImpl 都是class templates 而非 classes 时,情况有些特殊
没看懂,后面再回来看
评论区