swap 函数的编写

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014613043/article/details/51337065

swap 函数用来交换两个变量的值,C++ 标准库提供了这个函数。

前面的博客也有提到 copy and swap 技术,可以处理自我交换的情况。
那一个典型的实现就是这样的:

template<typename T>
void swap(T& a, T& b)
{
    T temp(a);
    a = b;
    b = temp;
}

*C++ 标准库就提供了这个函数,所以并不用自己去手动编写这段代码。

简单易懂的函数。

现在我们来讨论复杂一点的情况,如何设计一个swap 函数用来处理“以指针指向一个对象,内含真正的数据”这样的类型。
有点拗口是吧,嗯,那来看看代码是长什么样的:

struct Data
{
};

class SomeClass
{
    Data* ptd;
public:
    SomeClass()
    {
        ptd = new Data;
    }
    SomeClass(const SomeClass& ins)
    {
        ptd =  new Data(*ins.ptd);
    }
    ~SomeClass()
    {
        delete ptd;
    }
    SomeClass& operator = (const SomeClass& ins)
    {
        *ptd = *ins.ptd;  //复制时,只要让它们的指针指向的对象进行复制就可以了
        return *this;
    }
};

这种设计称为“pimpl 手法”(pointer to implementation)

这时候一旦置换两个对象,效率就没有那么高,因为我们想要的就是简单地置换内部的指针值而已。

那解决方法就是全特化(total template specialization)std 中的swap 函数。

namespace std
{
    template<>
    void swap<SomeClass>(SomeClass& a, SomeClass& b)
    {
        swap(a.ptd, b.ptd);//由于类的封装性,这个编译不通过
    }
}

有趣的是,在std 中,它并不允许你添加新的东西进去(建议这么做,如果你非要添加的话,它也只能两手一摊,被逼无奈),但是允许你特化标准的templates实现。

这是我们想要的,但是因为数据的封装性,还需要再修改一下,于是变成这样子:

struct Data
{
};

class SomeClass
{
    Data* ptd;
public:
    SomeClass()
    {
        ptd = new Data;
    }
    SomeClass(const SomeClass& ins)
    {
        ptd =  new Data(*ins.ptd);
    }
    ~SomeClass()
    {
        delete ptd;
    }
    SomeClass& operator = (const SomeClass& ins)
    {
        *ptd = *ins.ptd;  //复制时,只要让它们的指针指向的对象进行复制就可以了
        return *this;
    }

    void swap(SomeClass& b)
    {
        std::swap(ptd, b.ptd);  //指明使用std 的swap版本呢
    }
};
namespace std
{
    template<>
    void swap<SomeClass>(SomeClass& a, SomeClass& b)
    {
        a.swap(b);
    }
}

到目前为止,一切都挺好,我们也得到了想要的实现。

现在,我们再变一下,把SomeClass 变成模板,于是代码变成这样子:

template<typename Data> //现在Data 是typename 的参数
class SomeClass
{
    ...
};

namespace std //偏特化不被允许
{
    template<typename T>
    void swap<SomeClass<T> >(SomeClass<T> & a, SomeClass<T> & b)
    {
        a.swap(b);
    }
}

因为SomeClass是个模板类了,我们也相应地改变swap 函数。
但是,注意,C++ 并不允许函数偏特化(partially specialize),它只允许对class 进行偏特化。
解释一下:

template<typename T>
class Link
{};

template<typename T>
class FreeLink:public Link<T>  //类的偏特化,ok
{};

那怎么去编写一个swap 函数,用于模板化的Someclass 呢?
答案是,依然编写一个non-member 函数的swap , 去调用member 的swap 函数。但是不把它放入命名空间std 中


//前面不能有using namespace std;
//如果有,要么改swap 函数名称,要么另写一个自己的namespace ,把swap丢进去
template<typename T>
void swap(T& a, T& b)
{
    a.swap(b);
}

那现在存在的swap 版本函数有:

std::swap; 
swap; //自己编写的swap ,调用的时候,可以通过命名空间指明

[参考资料]
[1] Scott Meyers 著, 侯捷译. Effective C++ 中文版: 改善程序技术与设计思维的 55 个有效做法[M]. 电子工业出版社, 2011.
(条款25:考虑写出一个不抛异常的swap 函数)

展开阅读全文

没有更多推荐了,返回首页