条款25:考虑写出一个不抛出异常的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 implement”的缩写,我们针对内存结构比较大的类,设计了如下的方式:
class WidgetImpl{
public:
...
private:
int a,b,c;
std::vector<double> v;
...
};
namespace std{
template<>
void swap<Widget>(Widget& a,Widget& b){
a.swap(b);
}
class Widget{
public:
Widget(const Widget& rhs);
Widget& operator=(const Widget& rhs){
...
*pImpl=*(rhs.pImpl);
...
}
void swap(Widget& other){
using std::swap;
swap(pImpl,other.pImpl);
}
...
private:
WidgetImpl* pImpl;
};
这下我们对两个对象的交换就变成对指针进行交换,很明显速度会得到很大程度的提升,同时内存资源也可以没那么吃紧,显然这种方法更受欢迎!
C++的坑这么多?你以为这就完了?too naive了吧,如果两个class都是class template呢?怎么解决,即
template <typename T>
class WidgetImpl{...};
template <typename T>
class Widget{...};
那么,我们找一种解决方法吧!如下所示:
namespace WidgetStuff{
template <typename T>
class Widget{...}
template <typename T>
class WidgetImpl{...}
...
template<typename T>
void swap(Widget<T>& a,Widget<T> &b){
a.swap(b);
}
}
这下,我们想要交换两个Widget对象,C++会根据名称查找法则找出WidgetStuff中的Widget专属版本!
要点:
1)提供一个public swap成员函数,让它来高效极爱哦换你的类型的两个对象值,这个函数绝不该抛出异常;
2)在你的class或template所在的命名空间内提供一个non-member swap,并令它调用上述的swap成员函数;
3)如果你正编写一个class,为你的class特化std::swap,并令它调用你的swap成员函数。
总结:
1)当std::swap对你所设计的类型的效率不高时候,提供一个swap成员函数,并确定这个函数不抛出异常;
2)如果你提供一个member swap,也该提供一个non-member swap用来调用前者。对于classes也该特化std::swap;
3)调用swap时候应该针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”;
4)为“用户定义类型”进行std templates全特化是好的,但千万不要尝试在std中加入某些对std而言全新的东西!