相信大多数程序员都是用过swap函数,在STL库中也实现了这个函数。大致如下:
template<class T> void swap(T &a, T &b) { T temp(a); a = b; b = temp; }显然它完成了我们预期的功能,只要定义了复制构造函数以及赋值操作符的类型均可以使用该函数。
对于一般的内置类型或者是简单类型,还函数工作的很好,并且效率很高,可是在某些类型下,该swap版本的效率却非常的慢,这里引入一个词pimpl (pointer to implementation),从字面上来讲“以指针指向某个对象,该对象内涵真正数据”。显然,只要交换指针即可。下面举例说明:
//针对特定的用户自定义类型定义自己的swap函数 #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: StudentImpl *pStu; public: Student(string _name = "", int _age = 0) { pStu = new StudentImpl(_name, _age); } Student(StudentImpl *_pStu):pStu(_pStu){} Student(const Student &rhs); Student & operator=(const Student &rhs); ~Student() { delete pStu; } void swap(Student &b) { using std::swap; //使的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类中含有指针,那么它将 //具有指针所有的缺点。。具体请参见:智能指针 cout << "swap after : " << endl; pA.print(); pB.print(); return 0; }
执行结果为:
总结:
对于一般的交换操作,如果swap函数工作的很好,那么直接使用STL库中的即可,否则:
在类的内部定义一个swap函数执行真正的操作,并在该类中为你的class特化一个swap的版本, 令它调用你的swap成员函数。
如果你正在定义的是模板类,那么你的做法是:
在类的内部定义一个swap函数执行真正的操作,在类外使用一个non-member函数。调用该函数即可。注意该函数与你定义的类在同一个命名空间内。
关于模板类的swap函数的应用。请参见:http://blog.csdn.net/xuqingict/article/details/24117379
最后注意:你的swap函数决不可抛出异常,具体细节下次更新。
原文:http://blog.csdn.net/xuqingict/article/details/24115055