條款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而言全新的東西!