Effective C++ T25:考虑写出一个不抛异常的swap函数

38 篇文章 0 订阅
31 篇文章 2 订阅

Effective C++学习笔记总链接

改善程序与设计的55个具体做法学习笔记-每日1条


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

【技巧】

1. 当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常。

2. 如果你提供一个member swap,也该提供一个non-member swap用来调用前者,对于class(而非template),也请特化std::swap。

3. 调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”。(利用名称查找法则 为“T”型对象调用最佳的swap)

4. 为“用户定义类型”进行std template全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西。


典型的swap

swap是个很有用的函数,它是STL的一部分,后来成为异常安全性编程的脊柱,以及用来处理自我赋值可能性的一个常见机制

swap(置换)两对象值,意思是将两对象的值彼此赋予对方

其典型实现代码如下:

namespace std
{
	template<typename T>
	void swap(T& a, T& b) // 置换a和b的值
	{
		T temp(a);
		a = b;
		b = temp;
	}
}

缺省的swap的问题?

且看下面代码

class WidgetImpl
{
public:
	...
private:
	int a, b, c;           //可能有许多数据,
	std::vector<double> v; //意味复制时间很长。
	...
};

class Widget
{
public:
	Widget(const Widget& rhs);
	Widget& operator=(const Widget& ths) //复制Widget时,令它复制其WidgetImpl对象
	{
		...
		*pImpl = *(rhs.pImpl);
		...
	}
	...
private:
	WidgetImpl* pImpl; 
};

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

我们希望能够告诉std::swap,当Widget被置换时真正该做的是置换其内部的pImpl指针

解决办法:将std::swap针对Widget特化

【注意】:通常我们不能够(不被允许)改变std命名空间内的任何东西,但可以(被允许)为标准template(如swap)制造特化版本,使它专属于我们自己的class

具体做法:令Widget声明一个名为swap的public成员函数做真正的置换工作,然后将std::swap特化,令它调用该成员函数

class Widget // 与前同,唯一的区别是增加swap函数
{
public:
	...
	void swap(Widget& other)
	{
		using std::swap; // 这个声明是必要的
		swap(pImpl, other.pImpl); // 若要置换Widget就是置换其pImpl指针	
	}
	...
};

namespace std
{
	template<> // 这是std::swap针对“T是Widget的特化版本”
	void swap<Widget>(Widget& a, Widget& b)
	{
		a.swap(b); //若要置换Widget,调用其swap成员函数。
	}
}

值得说明的是,

  • template<> 表示它是std::swap的一个全特化版本
  • 函数名称之后的“< Widget>”表示这一特化版本系针对“T是Widget”而设计
  • 上述代码与STL容器有一致性,所有STL容器也都提供public swap成员函数和std::swap特化版本

针对Widget 是class template的swap, 等复习了template后补充


总结

首先,如果swap的缺省实现码对你的class或class template提供可接受的效率,不需要额外做任何事。

其次,如果swap缺省实现版的效率不足(一般你的class使用了某种pimpl手法(指向实现者的指针))尝试做以下事情:

  • 提供一个public swap成员函数,让它高效地置换你的类型的两个对象值,这个函数绝不该抛出异常
  • 在你的class或template所在的命名空间内提供一个non-member swap,并令它调用上述swap成员函数。
  • 如果你正编写一个class(而非class template),为你的class特化std::swap,并令它调用你的swap成员函数

最后如果你调用swap,请确定包含一个(using std::swap)声明式,以便让std::swap在你的函数内曝光,然后不加任何namespace修饰符,赤裸裸地调用swap

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值