天天看点

C++之考虑写出一个不抛出异常的swap函数(25)---《Effective C++》

条款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而言全新的东西!

继续阅读