相信大多数程序员都是用过swap函数,在STL库中也实现了这个函数。大致如下:
template<class T>
void swap(T &a, T &b)
{
T temp(a);
a = b;
b = temp;
}
显然它完成了我们预期的功能,只要定义了复制构造函数以及赋值操作符的类型均可以使用该函数。
对于一般的内置类型或者是简单类型,还函数工作的很好,并且效率很高,可是在某些类型下,该swap版本的效率却非常的慢,这里引入一个词pimpl (pointer to implementation),从字面上来讲“以指针指向某个对象,该对象内涵真正数据”。显然,只要交换指针即可。下面举例说明:
<span style="color:#ff0000;">//针对特定的用户自定义类型定义自己的swap函数</span>
#include <iostream>
using namespace std;
class StudentImpl
{
private:
string name;
int age;
public:
StudentImpl(string _name = "", int _age = 0):name(_name),age(_age){}
void print()
{
cout << "name : " << name << " age : " << age << endl;
}
};
class Student
{
private:
shared_ptr<StudentImpl> pStu;
public:
Student(string _name = "", int _age = 0)
{
pStu = make_shared<StudentImpl>(_name, _age);
}
Student(StudentImpl *_pStu):pStu(_pStu){}
Student(const Student &rhs);
Student & operator=(const Student &rhs);
~Student()
{
// delete pStu;
}
void swap(Student &b)
{
<strong><span style="color:#ff0000;">using std::swap;</span></strong> //使的STL库中的swap函数依然可用。
//也就是说,在编译器看到swap函数的时候,决定是调用为Student特化的版本呢,还是在某个命名空间中定义的版本呢,
//此处为StudentSpace命名空间,如果都没有,最后是否会调用STL库中的swap函数版本(就是这个using的功劳了),毕竟如果用户定义了自己的swap函数,肯定是要尽可能地
//使用自己的高效的版本。
swap(pStu, b.pStu);
}
void print()
{
pStu->print();
}
};
//Student实现
Student & Student::operator=(const Student &rhs)
{
pStu = rhs.pStu;
return *(this);
}
Student::Student(const Student &rhs):pStu(rhs.pStu)
{}
//如果我要交换两个Student对象, 那么其实只需要交换其内部成员pStu指针即可。
//在STL库中定义的swap函数版本并不知道这一点,
/*
template<class T>
void swap(T &a, T &b)
{
T temp(a);
a = b;
b = temp;
}
//显然只要类型T支持拷贝构造函数和复制操作符,那么上述swap就会实现预期的功能。
//但是对于Student类来说,将预示着3次复制构造函数的调用,显然我们可以为该类定义更高效的swap操作。
*/
//
/*
void swap(Student &a, Student &b)
{
using std::swap;
swap(a.pStu, b.pStu);
}
//此函数不能通过编译,因为pStu是类的private变量,因此,我们可以定义一个成员函数swap来执行真正的操作,再类外调用该成员函数完成该函数。
*/
//此函数调用成员函数swap完成。
namespace std //此处必须指明命名空间,因为此函数是对namespace中的swap函数的特化版本。
{
template<>
void swap<Student>(Student &a, Student &b)
{
a.swap(b); //通过对象a调用swap完成操作。
}
}
int main(void)
{
//StudentImpl a("AAA",1);
//StudentImpl b("BBB",2);
Student pA("AAA",1);
Student pB("BBB",2);
cout << "swap before : " << endl;
pA.print();
pB.print();
swap(pA,pB); // 如果此处的swap是STL库中原本的swap函数,那么这将会导致“指针悬垂”的问题,因为Student类中含有指针,那么它将
//具有指针所有的缺点。。具体请参见:<a target=_blank target="_blank" href="http://blog.youkuaiyun.com/xuqingict/article/details/21287209">智能指针</a>
cout << "swap after : " << endl;
pA.print();
pB.print();
return 0;
}
执行结果为:
总结:
对于一般的交换操作,如果swap函数工作的很好,那么直接使用STL库中的即可,否则:
在类的内部定义一个swap函数执行真正的操作,并在该类中为你的class特化一个swap的版本, 令它调用你的swap成员函数。
如果你正在定义的是模板类,那么你的做法是:
在类的内部定义一个swap函数执行真正的操作,在类外使用一个non-member函数。调用该函数即可。注意该函数与你定义的类在同一个命名空间内。
关于模板类的swap函数的应用。请参见:http://blog.youkuaiyun.com/xuqingict/article/details/24117379
最后注意:你的swap函数决不可抛出异常,具体细节下次更新。