[置顶] swap函数 一点儿都不简单

相信大多数程序员都是用过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.csdn.net/xuqingict/article/details/21287209">智能指针</a>
    cout << "swap after : " << endl;
    pA.print();
    pB.print();

    return 0;
}

执行结果为:

[置顶] swap函数 一点儿都不简单_第1张图片

总结:

对于一般的交换操作,如果swap函数工作的很好,那么直接使用STL库中的即可,否则:

在类的内部定义一个swap函数执行真正的操作,并在该类中为你的class特化一个swap的版本, 令它调用你的swap成员函数。

如果你正在定义的是模板类,那么你的做法是:

在类的内部定义一个swap函数执行真正的操作,在类外使用一个non-member函数。调用该函数即可。注意该函数与你定义的类在同一个命名空间内。

关于模板类的swap函数的应用。请参见:http://blog.csdn.net/xuqingict/article/details/24117379


最后注意:你的swap函数决不可抛出异常,具体细节下次更新。


你可能感兴趣的:(swap)