zoukankan      html  css  js  c++  java
  • 条款25:考虑写出一个不抛出异常的swap函数

    首先说下标准库的swap算法:

    1 namespace std{
    2     template<typename T>
    3     void swap(T & a, T & b)
    4     {
    5         T tmp = a;
    6         a = b;
    7         b = tmp;
    8     }
    9 }

    显然的,标准库实在是索然无味,自己随便写都能写出来,而且这样写有时候对于某些来说效率是特别低的。看看下面这个这个例子:

     1 class WidgetImpl{
     2 public:
     3     ...
     4 private:
     5     int a, b, c;
     6     std::vector<double> v;
     7     ...
     8 };
     9 class Widget{
    10 pubcic:
    11     Widget(const Widget & rhs);
    12     Widget & operator=(const Widget & rhs)
    13     {
    14         ...
    15         *pImpl = *(rhs.pImpl);
    16         ...
    17     }
    18 private:
    19     WidgteImpl * pImpl;
    20 };
    这个operator=配上上面那个swap一起的效率便非常的地下,本来交换Widget中Impl两者的指针就可以很高效的完成swap操作,但是这里硬是活生生的拷贝了两个对象。
    那么怎么完成高效的swap函数呢,首先想到的可能就是将标准库中的swap特例化,像下面这样:
    1 namespace std{
    2     template<> 
    3     void swap<Widget>(Widget & a, Widget & b)
    4     {
    5         swap(a.Impl, b.Impl);
    6     }
    7 }

    上面这个swap是标准库swap的total template specialization版本,但是他并不能通过编译,原因是因为其访问了对象的私有函数。正确的做法一般是下面这样:

     1 class Widget{
     2 public:
     3     ...
     4     void swap(Widget & other){
     5         using std::swap;
     6         swap(pUmpl, other.pUmpl);
     7     }
     8     ...
     9 };
    10 namespace std{
    11     template<> 
    12     void swap<Widget>(Widget & a, Widget & b)
    13     {
    14         a.swap(b);
    15     }
    16 }
    上面的做法与STL标准库也是高度一致,标准库的容器也都是有std版本的swap而且都在自己内部空间实现了swap供给前者调用。
    但是上述的做法在Widget是类模板的时候就不起作用了。因为下面的式子:
    namespace std{
        template<typename T>
        void swap<Widget<T> >(Widget<T> & a, Widget<T> & b)
        {
            a.swap(b);
        }
    }
    被c++规定为非法的。
    所以所,在无法进行模板特例化的情况下,就需要在特定的自己定义的名字空间创造一个swap函数(或者在std空间中重载一个版本,但是c++是不允许那样做的)。那么据类似下面这样:
     1 namespace WidgetStuff{
     2     ...
     3     template<typename T>
     4     class Widget{.......};
     5     ...
     6     template <typename T>
     7     void swap(Widget <T> & a, Widget<T> & b)
     8     {
     9         a.swap(b);
    10     }
    11 }
    这样就大功告成了。。
    这样,在任何的位置调用有关Widget的swap的时候,就会首先在WidgetStuff的名字空间里面寻找相应的swap。
    注意Widget的成员函数版本的swap中有一行是加上了下划线的,这个using声明语句的作用是即使当这个名字空间中的swap对Impl不能起到作用的时候,至少可以让Impl可以借助于std::swap来完成交换的操作。
     
    总结一下:
    如果因为系统默认版本的swap效率不足话,那么就要做下面这几件事:
        1. 提供一个class public swap对象,使其高效的置换两个特殊对象的值
        2. 在这个class或者template所在的命名空间提供一个non-memberswap,作用是来调用上述的
        swap.
        3.如果上述的class或者class template实际上是个class而非template,最好是特例化std::swap
        ,并且用这个来调用member的swap函数。
        4.在使用swap的时候,尤其是在自己定义的命名空间下,记得使用一个using 声明吧std::swap包含进来,这样即使在本命名空间中找不到合适的swap,至少也可以调用std的swap.且使用swap的时候不要再前面加上名字空间声明,这样前面声明的std::swap就白做了
     
    注意,成员版本的swap绝对不能抛出异常,但是非成员版本的swap函数是允许抛出异常的。前者不抛出异常的原因是其往往是针对内置类型的操作,而内置类型的操绝对不允许抛出异常。
     
  • 相关阅读:
    黑客书架上的书籍(转)
    vc 得到文件后缀名(转)
    配置IIS7(转)
    vs2008 目标框架 发布遇到的问题(转)
    CListCtrl用法(转)
    T400 折腾
    VS2008和.NET Framework3.5新功能(转)
    sql 2008 ctp 安装
    关于定位lsass内存中的明文密码(转)
    NT系统下木马进程的隐藏与检测(转)
  • 原文地址:https://www.cnblogs.com/-wang-cheng/p/4862988.html
Copyright © 2011-2022 走看看