天天看點

C++之考慮寫出一個不抛出異常的swap函數(25)---《Effective C++》

條款25:考慮寫出一個不抛出異常的swap函數

對于swap的一般是實作,我們可以參考以下代碼,這也是預設情況下标準庫提供的swap算法:

namespace std{
    template<typename T>
    void swap(T& a,T& b){
        T temp(a);
        a=b;
        b=temp;
    }
}
           

隻要類型T支援copying(通過copy構造函數和copy assignment操作符完成),預設的swap是吸納代碼就會幫你置換類型為T的對象那個,你不需要為此另外在做任何工作!

那麼,預設的swap函數可能有什麼問題呢?我們看到在上面代碼中,進行了三個對象的複制,如果對象占用的記憶體結構比較大呢?複制操作很費時間、将會吃掉很大記憶體,是以,我們想要設計一種節約資源的方式!

為了解決這個問題,我們采用了“pimpl手法”,pimpl手法是“pointer to implement”的縮寫,我們針對記憶體結構比較大的類,設計了如下的方式:

class WidgetImpl{
public:
    ...
private:
    int a,b,c;
    std::vector<double> v;
    ...
};
           
namespace std{
    template<>
    void swap<Widget>(Widget& a,Widget& b){
        a.swap(b);
    }
class Widget{
public:
    Widget(const Widget& rhs);
    Widget& operator=(const Widget& rhs){
    ...
    *pImpl=*(rhs.pImpl);
    ...
    }
    void swap(Widget& other){
        using std::swap;
        swap(pImpl,other.pImpl);
    }
    ...
private:
    WidgetImpl* pImpl;
};
           

這下我們對兩個對象的交換就變成對指針進行交換,很明顯速度會得到很大程度的提升,同時記憶體資源也可以沒那麼吃緊,顯然這種方法更受歡迎!

C++的坑這麼多?你以為這就完了?too naive了吧,如果兩個class都是class template呢?怎麼解決,即

template <typename T>
class WidgetImpl{...};
template <typename T>
class Widget{...};
           

那麼,我們找一種解決方法吧!如下所示:

namespace WidgetStuff{
    template <typename T>
    class Widget{...}
    template <typename T>
    class WidgetImpl{...}
    ...
    template<typename T>
    void swap(Widget<T>& a,Widget<T> &b){
        a.swap(b);
    }
}
           

這下,我們想要交換兩個Widget對象,C++會根據名稱查找法則找出WidgetStuff中的Widget專屬版本!

要點:

1)提供一個public swap成員函數,讓它來高效極愛哦換你的類型的兩個對象值,這個函數絕不該抛出異常;

2)在你的class或template所在的命名空間内提供一個non-member swap,并令它調用上述的swap成員函數;

3)如果你正編寫一個class,為你的class特化std::swap,并令它調用你的swap成員函數。

總結:

1)當std::swap對你所設計的類型的效率不高時候,提供一個swap成員函數,并确定這個函數不抛出異常;

2)如果你提供一個member swap,也該提供一個non-member swap用來調用前者。對于classes也該特化std::swap;

3)調用swap時候應該針對std::swap使用using聲明式,然後調用swap并且不帶任何“命名空間資格修飾”;

4)為“使用者定義類型”進行std templates全特化是好的,但千萬不要嘗試在std中加入某些對std而言全新的東西!

繼續閱讀