条款25:考虑写出一个不抛异常的swap函数

//*********************************************************************************

swap原本只是STL的一部分,后来成为异常安全性编程(exception-safe programming)的脊柱,以及用来处理自我赋值可能性的一个常见机制

/**************************************************************
//标准函数库提供的函数算法如下:
namespace std
{
template<typename T>
void swap(T& a, T& b)
{
T temp(a);
a = b;
b = temp;
}
}
//只要类型T支持copying, 你不需要再为此做任何工作


//标准swap函数的复制动作太多,不高效,最主要的是那些“以指针指向对象,内含真正数据”的类型
//最常见的形式是所谓的pimpl( pointer to implementation )手法,示例:
class WidgetImpl
{
public:
...
private:
int a, b, c; //可能有很多数据
std::vector<double> v; //意味着复制时间很长
...
};
class Widget
{
public:
Widget(const Widget& rhs);
Widget& operator=(const Widget& rhs)
{
...
*pImpl = *(rhs.pImpl);
...
}
private:
WidgetImpl* pImpl; //指针,所指对象内含Widget数据
};


//一旦要置换两个Widget对象值,我们唯一需要做的就是置换其pImpl指针
//但默认的swap不知道这一点,它不只复制三个Widget,还复制两个WidgetImpl对象,非常缺乏效率


//将std::swap针对Widget全特化,基本构想的示例,该示例不能通过编译
class Widget;
namespace std
{
template<> //这是std::swap针对T是Widget的特化版本
void swap<Widget>(Widget& a, Widget& b)
{
swap(a.pImpl, b.pImpl); //不能通过编译因为pImpl是私有的
}
}
//通常我们不能够改变std空间中的任何东西,但可以为标准templates制造特化版本,使它专属于我们自己的class(如Widget)


//完整版本如下:
class Widget
{
public:
...
void swap(Widget& other)
{
using std::swap; //这个声明很有必要
swap(pImpl, other.pImpl);
}
...


};
namespace std
{
template<>
void swap<Widget>(Widget& a, Widget& b)
{
a.swap(b);
}


}
//这个做法不仅能通过编译,还与STL容器有一致性,因为所有STL容器都提供有public swap成员函数和std::swap特化版本(用以调用前者)


//如果假设Widget和WidgetImpl都是class templates而不是classes, 也许我们可以试着将WidgetImpl内的数据加以参数化:
template<typename T>
class Widget{};
template<typename T>
class WidgetImpl{};
namespace std
{
template<typename T>
void swap<Widget<T>>(Widget<T>& a, Widget<T>& b) //不合法,因为函数模板不能偏特化
{
a.swap(b);
}
}


//当你打算偏特化一个函数模板时,惯常的作法是简单地为他添加一个重载的函数模板,示例如下:
namespace std
{
template<typename T>
void swap(Widget<T>& a, Widget<T>& b) //这是std::swap的一个重载版本, swap之后没有<..>,但是这个仍然不合法,std空间特殊不允许添加新的...
{
a.swap(b);
}
}
//一般而言重载函数模板没有问题,但是std是一个特殊的命名空间,其管理规则也比较特殊。
//客户可以全特化std里的template,但是不能添加新的templates(或其它classes或functions或其它任何东西)到std里头
//std的内容完全由C++标准委员会决定,标准委员会禁止我们膨胀那些已经声明好的东西
//如果你希望你的软件有可预期的行为,请不要添加任何东西到std里头


//解决办法:还是声明一个non-member swap函数,让他调用member swap函数,但不再将那个non-member swap函数声明为std::swap的特化版或者重载版, 示例:
namespace WidgetStuff
{
template<typename T>
class Widget{};
template<typename T>
class WidgetImpl{}; //内含swap函数


template<typename T> //non-member函数,但是并不属于std命令空间
void swap(Widget<T>& a, Widget<T>& b)
{
a.swap(b);
}
}
//现在任何地点的任何代码打算置换两个Widget对象,因而调用swap,C++的命名查找法则会找到WidgetStuff内的swap专属版本
//这个做法对classes和class templates都可以行得通,但是针对class的特化版本还是需要的


//!! 成员版swap绝对不可以抛出异常


***************************************************************/


//特化:编译器认为对于特定的类型,如果你对某一功能有更好的实现,那么就该听你的
//模板分为类模板和函数模板,特化分为全特化和偏特化。
//全特化:限定死模板实现的具体类型
//偏特化:如果这个模板有多个类型,那么只限定其中的一部分
//类模板能全特化也能偏特化,函数模板只能全特化不能偏特化


//完整示例如下:
/***************************************************************************
#include <vector>
namespace WidgetStuff
{
template<typename T>
class WidgetImpl
{
private:
int a, b, c;
std::vector<double> v;
};


template<typename T>
class Widget
{
public:
void swap(Widget& w)
{
using std::swap;
swap(pImpl, w.pImpl);
}
private:
WidgetImpl<int>* pImpl;
};


template<typename T>
void swap(Widget<T>& a, Widget<T>& b)
{
a.swap(b);
cout<<"使用non-member版本\r\n";
}
}


class TC
{
public:
void swap(TC& t)
{
using std::swap;
swap(pImpl, t.pImpl);
}
private:
WidgetStuff::WidgetImpl<int>* pImpl;
};


namespace std
{
template<>
void swap(TC& a, TC& b)
{
a.swap(b);
cout<<"使用std内的特化版本\r\n";
}
}
*********************************************************************************/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值