Effective C++条項25

3900 ワード

異常を投げ出さないswap関数を書くことを考慮します
このセクションでは、効率的なswap関数をカスタマイズする方法について説明します.
std名空間におけるswapデフォルト関数は次のようになります.
namespace std{
    template<typename T>
    void swap(T& a, T& b)
    {
        T temp(a);
        a=b;
        b=temp;
    }
}

class WidgetImpl{
public:
    ……
private:
    int a,b,c;              //    ,        
    std::vector<double> b;
    ……
};

著者らは,交換対象のデータが大きい場合,以上のswapの実行効率が遅くなることを見ることができ,このような状況を変えるために,以下の方法を提案した.
クラスの再定義
class Widget{
public:
    Widget(const Widget& rhs);
    Widget& operator=(const Widget& rhs
    {
        ……          //  Widget ,  WidgetImpl   
        *pImpl=*(ths.pImpl);
        ……
    }
    ……
private:
    WidgetImpl* pImpl;//  ,  Widget   
};

2つのWidgetオブジェクトの値を置き換え、そのpImplポインタを置き換えるだけで効率が向上します.では、swapをどのように定義しますか?次のようになります.
calss Widget{
public:
    ……
    void swap(Widget& other)
    {
        using std::swap;//       
        swap(pImpl, other.pImpl);
    }
    ……
};
namespace std{
    template<> //    swap  
    void swap<Widget>(Widget& a, Widget& b)
    {
        a.swap(b);  //       
    }
}

Widget w1,w2;

swap(w1,w2);//         Widget   swap.

上記のコードから,我々の目的を達成し,2つのWidgetImplオブジェクトの交換を実現し,ポインタのみを交換することが分かった.もちろんここでは交換クラスWidgetを構築する必要があり,Widgetタイプのswapをフル特化する必要があり,関数体内でmember関数swapを定義する必要がある.
では、読者は迷って、大きな周りを回って、どのように直接メンバー関数swapを呼び出さないのか、これはあなたがswap(a,b)文を使うと、自動的に適切に一致する機能を持っているからです.お客様の使用に適しており、インタフェースの一貫性と簡易性を実現します.
次に、2つのクラスがtemplateである場合、上記の問題をどのように解決するかを紹介します.次のようになります.
template<typename T>
class WidgetImpl{……};
template<typename T>
class Widget{……};

名前空間を次のように再命名できます.
namespace WidgetStuff{
   ……//    WidgetImpl 
   template<typename T>//  swap  
   class Widget{……};
   ……
   template<typename T>
   void swap(Widget<T>& a,//non-member,   std    
             Widget<T>& b)
   {
       a.swap(b);
   }
}

Widget<int> w1,w2;

swap(w1,w2);//     WidgetStuff Widget<int>   swap.