考虑实现一个不抛异常的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呢?总结一下:
- 提供一个更加高效的,不抛异常的共有成员函数(比如
Widget::swap
)。 - 在你类(或类模板)的同一命名空间下提供非成员函数
swap
,调用你的成员函数。 - 如果你写的是类而不是类模板,请偏特化
std::swap
,同样应当调用你的成员函数。 - 调用时,请首先用
using
使std::swap
可见,然后直接调用swap
。