zoukankan      html  css  js  c++  java
  • 考虑实现一个不抛异常的swap

    Effective C++:参考自harttle land

    类的swap实现与STL容器是一致的:提供swap成员函数, 并特化std::swap来调用那个成员函数

     1 class Widget {
     2 public:       
     3   void swap(Widget& other){
     4     using std::swap;          // 使得`std::swap`在该作用域内可见
     5     swap(pImpl, other.pImpl);
     6   }
     7 };
     8 
     9 namespace std {
    10   template<>
    11   void swap<Widget>(Widget& a, Widget& b){
    12       a.swap(b);              // 调用成员函数
    13   }
    14 }

    类模板的Swap

    1.下面代码不能通过编译。C++允许偏特化类模板,却不允许偏特化函数模板

    template<typename T>
    class WidgetImpl { ... };
    
    template<typename T>
    class Widget { ... };
    
    namespace std {
        template<typename T>
        // swap后的尖括号表示这是一个特化,而非重载。
        // swap<>中的类型列表为template<>中的类型列表的一个特例。
        void swap<Widget<T> >(Widget<T>& a, Widget<T>& b){
            a.swap(b); 
        }
    }

    2. 不能向STL里面添加新的东西,重载版本。C++标准中,客户只能特化std中的模板,但不允许在std命名空间中添加任何新的模板,会引发未定义

    namespace std {
        template<typename T>
        // 注意swap后面没有尖括号,这是一个新的模板函数。
        // 由于当前命名空间已经有同名函数了,所以算函数重载。
        void swap(Widget<T>& a, Widget<T>& b){
            a.swap(b); 
        }
    }

    正确做法是不在std下添加swap函数了,把swap定义在Widget所在的命名空间中,或直接在global命名空间:

    namespace WidgetStuff {
       template<typename T> 
       class Widget {
       public:       
          void swap(Widget& other){
            using std::swap;         
            swap(pImpl, other.pImpl);
      }
    };
      
        template<typename T> 
        void swap(Widget<T>& a, Widget<T>& b){
            a.swap(b);
        }
    }

    似乎类的Swap也只需要在同一命名空间下定义swap函数,而不必特化std::swap。 但是!有人喜欢直接写std::swap(w1, w2),调用std::swap,WidgetStuff::swap不会得到调用。特化std::swap可以让你的类更加健壮。此时,C++编译器会优先调用指定了T的std::swap特化版,其次是T所在命名空间下的对应swap函数, 最后才会匹配std::swap的默认实现。

    应用:

    赋值构造函数的swap写法:解决自己赋值自己的问题

    class Widget
    {
    public:
        Widget():id(0), size(0), data(NULL){}
        Widget(int val)
        {
            id = val;
            size = id+1;
            data = new int [size];
        }
        Widget(const Widget &rhs) //deep copy
        {
            id = rhs.id;
            size = rhs.size;
            data = new int[size];
            memcpy(data, rhs.data, sizeof(int)*size);
        }
        friend void swap(Widget & lhs, Widget &rhs)
        {
            using std::swap;
            swap(lhs.id, rhs.id);
            swap(lhs.size, rhs.size);
            swap(lhs.data, rhs.data);
        }
      /* Widget
    & operator = (Widget rhs) //pass by value, copy构造后swap { swap(*this, rhs); return *this; } */ Widget& operator= (const Widget &rhs) //pass by 引用 { Widget tmp(rhs); swap(*this, tmp); return *this; } ~Widget() { std::cout<<"id:"<< id << " destructor called..." << std::endl; if(data != NULL) { delete [] data; data = NULL; } } int getId() const { return id; } int getSize() const { return size; } private: int *data; int id; int size; };

    如何实现Swap呢?总结一下:

    1. 提供一个更加高效的,不抛异常的共有成员函数(比如Widget::swap)。
    2. 在你类(或类模板)的同一命名空间下提供非成员函数swap,调用你的成员函数。
    3. 如果你写的是类而不是类模板,请偏特化std::swap,同样应当调用你的成员函数。
    4. 调用时,请首先用using使std::swap可见,然后直接调用swap
  • 相关阅读:
    关于bind named.conf.options
    MASM 16位汇编程序几种典型的格式
    About GCC
    WebForms UnobtrusiveValidationMode 需要“jQuery”ScriptResourceMapping。
    Linux系统下的shutdown命令用于安全的关闭/重启计算机
    TreeView.ImageSet 属性
    python seaborn
    python neo4j
    Impala与Hive的比较
    pandas.resample()
  • 原文地址:https://www.cnblogs.com/demian/p/9301679.html
Copyright © 2011-2022 走看看