zoukankan      html  css  js  c++  java
  • [Effective C++ --025]考虑写出一个不抛异常的swap函数

    引言

    在我的上一篇博客中,讲述了swap函数。

    原本swap只是STL的一部分,而后成为异常安全性编程的脊柱,以及用来处理自我赋值可能性。

    一、swap函数

    标准库的swap函数如下:

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

    只要类型T支持拷贝(通过拷贝构造函数和拷贝赋值操作符完成),那么这个版本的swap函数就会帮你置换类型为T的对象。

    二、Pimpl情境下的swap函数

    假设我们有如下的类。

     1 class WidgetImpl {
     2 public:
     3     ...
     4 private:
     5     int a,b,c;                        // 可能有许多数据
     6     std::vector<double> v;            // 意味复制时间很长
     7     ....
     8 }
     9 
    10 class Widget {
    11 public:
    12     Widget(const Widget& rhs);
    13     Widget& operator = (const Widget& rhs)
    14     {
    15          .....
    16          *pImpl = *(rhs.pImpl);
    17      }  
    18 private19     WidgetImpl* pImpl;
    20 };

    一旦要置换两个Widget对象,我们唯一需要做的就是置换其pImpl指针,但是一种的swap函数不知道这一点,它会复制三个Widget和三个WidgetImpl,效率非常低下!

    所以我们希望的效果是:只交换pImpl指针!

    因此我们可以设计出这个思路:

    1 namespace std {
    2     template<>          // 表示它是std::swap的一个特化版本
    3     void swap<Widget>(Widget &a, Widget& b)
    4     {
    5          swap(a.pImpl, b.pImpl);
    6     }
    7 }

    上述代码当然通不过编译,因为pImpl是私有成员。或许你说我们可以用friend函数来解决这一问题,可是这不大符合预期!

    那我们再包装一下:

     1 class Widget {
     2 public:
     3     ...
     4     void swap(Widget& other)
     5     {
     6         using std::swap;
     7         swap(pImpl, other.pImpl);      // 若置换Widgets就置换其pImpl指针
     8     }
     9     ....
    10 }
    11 
    12 namespace std {
    13     template<>                         // 表示它是std::swap的一个特化版本
    14     void swap<Widget>(Widget& a, Widget& b)
    15     {
    16          a.swap(b);
    17     }
    18 }

    这样不仅通过编译,还与STL容器具有一致性,因为所有的STL容器也都提供有public swap成员函数和std::swap特化版本。

    但需要注意的是:C++只允许对class templates偏特化,在function template上特化是行不通的。如下面代码是编译不过的。

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

    三、function template偏特化

    那么,如果我们想在function template上特化,应该怎么做呢?答案是添加一个重载版本!

    如下:

    1 namespace std {
    2     template<typename T>         
    3     void swap(Widget<T>& a,            // 注意这里没有“<Widget<T>>”
    4               Widget<T>& b)
    5     {
    6          swap(a.pImpl, b.pImpl);
    7     }
    8 }

    同时,为了其他人调用swap时能取得我们提供的较高版本的template特定版本,我们可以声明一个non-member函数!

     1 namespace Widget {
     2     template<typename T>   
     3     class Widget{....}
     4     
     5     template<typename T>         
     6     void swap(Widget<T>& a,            // 注意这里没有“<Widget<T>>”
     7               Widget<T>& b)
     8     {
     9          swap(a.pImpl, b.pImpl);
    10     }
    11 }

    ◆总结

    1.当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常。

    2.如果你提供一个member swap,也该提供一个non-member swap用来调用前者。对于classes,也请特化std::swap。

    3.调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”。

    4.为“用户定义类型”进行std template全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西

  • 相关阅读:
    Matlab 实现神经网络实例
    python 神经网络实例
    TensorFlow基础笔记(2) minist分类学习
    贝叶斯深度学习
    python:一行代码实现局域网共享文件
    maven:手动上传jar私服
    maven:清除lastUpdated文件
    python:序列化与反序列化(json、pickle、shelve)
    jenkins变量的传递
    python:解析requests返回的response(json格式)
  • 原文地址:https://www.cnblogs.com/hustcser/p/4186746.html
Copyright © 2011-2022 走看看