zoukankan      html  css  js  c++  java
  • effective C++ 条款25 swap

    item 25:一个不抛异常的swap函数

    标准库有一个swap用于交换两个对象值

    namespace std{

        template<typename T>

        void swap(T& a , T& b)

        {

            T temp(a) ;

            a = b ;

            b = temp ;

        }

    }

    所以,只要对象的类型T支持copying(copy ctor 和 copy assignment),那么你就可以用这个标准库的swap,但是,你必须保证,你swap的时候,上述操作是你需要的,有的时候,没有必要,尤其是pimpl(pointer to implementation)手法。

    例如下面这个例子:

    class WidgetImpl{

    public :

        ....

    private :

        int a , b , c  ;

        ....   //好多成员

    };

    class Widget

    {

    public:

       Widget(const Widget & rhs) ;

        Widget& operator=(const Widget & rhs)

        {

             *pImpl = *rhs.pImpl // 复制所指的对象

        }

    private:

        WidgetImpl * pImpl ;

    };

    一旦要交换两个Widget,我们其实就让两个指针换一下就可以了,没有必要把指针所指的内容互换,那样效率太低。

    想法1:

    namespace std{

        template<>  // 全特化

        void swap<Widget> (Widget & a,  Widget & b)

        {

            swap(a.pImpl , b.pImpl) ;

        }

    }

    上面这个想法编译不过,因为pImpl是private的,是不能被外界访问的。

    想法2 : 加入类的成员函数swap,让模板函数去调用

    class Widget{

    public :

        void swap(Widget & other)

        {

            using std::swap ;  // 后面讲

            swap(p.Impl , other.pImpl) ;

        }

        ...

    };

    namespace std{

        template<>  // 全特化

        void swap<Widget> (Widget & a,  Widget & b)

        {

            a.swap(b) ;

        }

    }

    通过上面的写法,我们就能得到正确的,针对类别Widget 的swap函数。这就叫为你的class特化std::swap,并令它调用你的swap函数。

    但是,问题,如果我现在是类模板,怎么办?

    现在的类是:

    template<typename T>

    class WidgetImpl{...}

    template<typename T>

    class Widget{...}

    如果你现在这么做:

    namespace std{

        template<typename T>  // 偏特化 

        void swap<Widget> (Widget<T> & a,  Widget<T> & b)

        {

            a.swap(b) ;

        }

    }

    sorry不行了,原因有以下2点:

    1、因为C++只允许对class template进行偏特化,不对能function template进行偏特化。

    2、在std这个命名空间,不可以添加新的template到std里。上面,我们能加东西到std是因为,为标准的templates(eg : swap)制造特化版本。

    为了解决上述2两个问题,引入下面的解决方法

    想法3:转为class template量身打造

    定义一个专门的命名空间,放入我们所有的东西

    namespace WidgetStuff{

        template<typename T>

        class WidgetImpl{...}

        template<typename T>

        class Widget{...} // 这个里面有一个swap函数

        template<tyoename T>

        void swap(Widget<T>& a , Widget<T>& b)

        {

             a.swap(b) ;  

        }

    }

    我们在用swap函数的时候,应该这么用:

    template<typename T>

    void doSomething(T& obj1 , T& obj2)

    {

        using std::swap ;  // 这样即使没有T的专属版本的swap函数 

        swap(obj1 , obj2)  ;

    }

    上面就结束了我们swap函数的制作过程,那么为什么可行呢?这个都是利用了C++的名称查找规则

    编译器用“实参取决之查找规则”,找出WidgetStuff中的swap函数,如果命名空间中没有T的专属swap函数,编译器就用std::swap,即使如此,编译器还是喜欢std::swap重T的专属版本,就是想法2会选中。所以,优先级顺序是想法3--》想法2--》标准库自己的swap

    总结:

    1、弄清楚什么时候我们需要自己写swap函数

    2、在class 或者 class template提供一个public swap函数

    3、在class 或者 class template的命名空间中提供一个non-member swap ,调用2中的swap

    4、如果你正在编写一个class(不是template),为class特化std::swap。并令他调用2中的swap

    5、当你实际用swap时,先using std::swap,然后不加任何修饰符,赤裸裸调用swap

    成员版swap决不能抛出异常,因为很多异常安全性代码都是通过类的swap成员函数保证的,并且,我们自己去写swap,一般都是用指针,内置类型,这些类型不会抛异常,写swap是为了高效

  • 相关阅读:
    关于影像颜色替换
    DevExpress TreeList控件的复选框
    深刻理解Vue中的组件
    【Vue】组件的基础与组件间通信
    vue语法糖
    vue中prop传值时加不加v-bind(冒号:)
    Vue从入门到实战
    vue-router实现原理
    Vue学习笔记——Vue-router
    Vue-router 学习笔记
  • 原文地址:https://www.cnblogs.com/youxin/p/4300804.html
Copyright © 2011-2022 走看看