swap介绍
- swap作为STL的一部分,在算法、容器、迭代器中被广泛使用。
c++为用户定义了算法的默认形式如下:如果类型T支持拷贝构造和拷贝赋值就可以完成交换。1
2
3
4
5
6
7
8namespace std{
template<typename T>
void swap(T& x, T& y){
T tmp(x);
x = y;
y = tmp;
}
}
内含指针的类型交换
从上面可以看出,默认版本需要执行一次拷贝构造,两次拷贝赋值才能完成交换,如果类型内部像下面这种pimpl(内含指针,指向数据)的形式,显然只需要交换指针即可,如果采用默认版本就回降低效率。
1
2
3
4
5
6
7
8
9
10
11
12
13
14class A_impl{
int val;
};
class A{
A_impl* p;
public:
A(){};
inline bool operator=(const A& rhs){
//do something
*p = *(rhs.p);
//do something
}
inline A(const A& rhs);
};- 很自然地,我们可以通过函数重载来在类内自定义swap函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32class A_impl{
public:
int val;
A_impl(int val):val(val){};
};
class A{
A_impl* p;
public:
A(){};
inline bool operator=(const A& rhs);
inline A(const A& rhs);
inline A(A_impl* p):p(p){};
void swap(A& rhs){
std::swap(p, rhs.p);
}
inline int getVal(){
return p -> val;
}
};
namespace std{
template<>
void swap<A>(A& lhs, A& rhs){
lhs.swap(rhs);
}
}
A* d1 = new A(new A_impl(0));
A* d2 = new A(new A_impl(3));
cout << d1->getVal() << "--" << endl; //0
cout << d2->getVal() << "--" << endl; //3
swap(d1, d2);
cout << d1->getVal() << "--" << endl; //3
cout << d2->getVal() << "--" << endl; //0
swap函数重载和特化
- 对于模板类,如何设计swap
假如我们现在有个模板类
1 | template<typename T> |
我们希望写出它的特化版本,但是很不幸会报错
1 | namespace std{ |
通常情况下我们是偏特化一个函数模板:
1 | namespace std{ |
但是c++的std命名空间很特殊,如果不必要,尽量不要在其中添加自定义的操作。那么我们该怎么让程序调用我们自己的版本呢?很简单,定义一个非成员swap,并置于某个命名空间中:
1 | namespace hqin{ |
下面看看我们在调用下面这个函数时会发生肾么事
1 | template<typename T> |
执行到swap后编译器有很多不同版本可以选择:
- std一般化版本
- std特化版本
- 某个命名空间中的T专属版本
事实情况是编译器会优先匹配global空间或T所在命名空间中的所有T专属的版本,如果不存在就使用std特化版本,最后使用一般版本。
swap异常
- 成员版的swap绝不可抛出异常。因为swap的一个最好的应用就是帮助class提供强烈的一场安全性保障。
- 非成员版本可以抛出异常。