条款25:考虑写出一个不抛异常的swap函数

条款25:考虑写出一个不抛异常的swap函数
(Consider support for a non-throwing swap.)

内容:
    swap函数就是相互交换两个对象的内容,标准程序库中已经提供了该函数的实现版本,其实现过程也是那么的如你预
期所愿:
    namespace std{
        template <typename T>
        void swap(T& a,T& b)
        {
            T tempt(a);
            a = b;
            b = tempt;
        }
    }
    但是这种实现版本对某些类型来说,效率上存在很大的问题,尤其面临那些对的性能要求很高的软件,这种实现版本
显然不能满足要求.这里我们所说的'某些类型'大体上指的是'以指针指向一个对象,内含真正数据'那种类型,这种
设计的常见的表现形式就是所谓的"pimpl"手法(条款31将提到).我们再来看一个例子:
    class Window{ //这个class使用了pimpl手法
    public:
        Window(const Window& rhs);
        Window& operator=(const Window& rhs){
            ...
            *pImpl_ = *(rhs.pImpl_);
            ...
        }
        ...
    private:
        class WindowImpl{
        public:
            ...
        private:
            string title_;
            std::vector<Window* > childList_;
        };
        WindowImpl* pImpl_;
    };
    如果用std::swap进行对两个Window对象进行交换的话,那么它不止复制三个Window还复制了三个WindowImpl对象,
非常缺乏效率.通常我们不能够(不被允许)改变std命名空间内的任何东西,但可以为标准templates制造特化版本,这里
我们可以为std::swap提供一个全特化版本来提高效率:
    namespace std{
        template<>
        void swap<Window>(Window& a,Window& b){
            swap(a.pImpl_,b.pImpl_);
        }
    }
    但是这里遇到的问题是pImpl_是private域内成员变量,我们无法直接访问它,怎么办?我们的做法是构造一个member
swap的版本.
    class Window{
    public:
        ...
        void swap(Window& other){
            using std::swap;
            swap(pImpl_,other.pImpl_);
        }
        ...
    };
    namespace std{
        template<>
        void swap<Window>(Window& a,Window& b){
            a.swap(b);
        }
    }
    倘若这里的Window以及WindowImpl要都是class template而非class:
    template<typename T>
    class WindowImpl{...};
    template<typename T>
    class Window{...};
    那么我们就不能这么写了:
    namespace std{
        template<typename T>
        void swap<Window<T>>(Window<T>& lhs,Window<T>& rhs){
            a.swap(b);
        }
    }
    因为我们企图偏特化一个function template,但C++只允许对class template偏特化,在function template身上偏
特化是行不通的.那这么办?没有其它方法了吗?有!当你打算偏特化一个function template时,一般做法是简单地为它
添加一个重载版本,比如:
    namespace std{
        template<typename T>
        void swap(Window<T>& lhs,Window<T>& rhs){ //这里是一个重载版本(没有<...>)
            lhs.swap(rhs);
        }
    }
    这样重载function templates没有问题,但std是个特殊的命名空间,你可以全特化std内的template,但你不能添加
心的template到std中,因为改命名空间内容完全由C++标准委员会决定,标准委员会禁止我们膨胀那些已经声明好的东
西.晕死,这也不行,那我们就自己创建命名空间不就行了:
    namespace WindowStuff{
        ...
        template<typename T>
        class Window{...};
        ...
        template<typename T>
        void swap(Window<T>& lhs,Window<T>& rhs){
            a.swap(b);
        }
    }
    那接下来我们的客户如何使用呢?假设你正在写一个function template,其内需要置换两个对象值:
    template<typename T>
    void doSomething(T& obj1,T& ojb2){
        using std::swap;
        ...
        swap(obj1,obj2);
        ...
    }
    一旦编译器看到对swap的调用,它们便查找适当的swap并调用.如果T是Window并位于命名空间WindowStuff内的,
编译器会使用'参数依赖查找法'找到WindowStuff命名空间内的swap版本,如果该空间没有对应的swap版本,编译
器就使用std::swap(using的声明式让std::swap在函数内曝光),而在std空间内如果你提供了针对T的特化版本,编
译器就会挑中它,当然如果没有提供,那么编译器只有老老实实的调用那个一般化的swap版本的啰,呵呵.
    现在来说一下我们的条款内容:成员版swap绝不可抛出异常.那是因为swap的一个最好的应用是帮助classes(和
class template)提供强烈的异常安全性保障.此约束只施行于成员版.
    今天说的有点多了,如果没看懂的话,请慢慢消化.
    请记住:
    ■ 当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常.
    ■ 如果你提供一个member swap,也该提供一个non-member swap用来调用前者.对于classes(而非template),
也请特化std::swap.
    ■ 调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何""命名空间资格修饰.
    ■ 为"用户定义类型"进行std templates全特化是好的,但千万不要尝试在std内加入某些std而言全新的东西.

你可能感兴趣的:(条款25:考虑写出一个不抛异常的swap函数)