effective C++ 条款25 swap
item 25:一个不抛异常的swap函数
标准库有一个swap用于交换两个对象值
namespace std{
template<typename T>
void swap(T& a , T& b)
{
T temp(a) ;
a = b ;
b = temp ;
}
}
所以,只要对象的类型T支持copying(copy ctor 和 copy assignment),那么你就可以用这个标准库的swap,但是,你必须保证,你swap的时候,上述操作是你需要的,有的时候,没有必要,尤其是pimpl(pointer to implementation)手法。
例如下面这个例子:
class WidgetImpl{
public :
....
private :
int a , b , c ;
.... //好多成员
};
class Widget
{
public:
Widget(const Widget & rhs) ;
Widget& operator=(const Widget & rhs)
{
*pImpl = *rhs.pImpl // 复制所指的对象
}
private:
WidgetImpl * pImpl ;
};
一旦要交换两个Widget,我们其实就让两个指针换一下就可以了,没有必要把指针所指的内容互换,那样效率太低。
想法1:
namespace std{
template<> // 全特化
void swap<Widget> (Widget & a, Widget & b)
{
swap(a.pImpl , b.pImpl) ;
}
}
上面这个想法编译不过,因为pImpl是private的,是不能被外界访问的。
想法2 : 加入类的成员函数swap,让模板函数去调用
class Widget{
public :
void swap(Widget & other)
{
using std::swap ; // 后面讲
swap(p.Impl , other.pImpl) ;
}
...
};
namespace std{
template<> // 全特化
void swap<Widget> (Widget & a, Widget & b)
{
a.swap(b) ;
}
}
通过上面的写法,我们就能得到正确的,针对类别Widget 的swap函数。这就叫为你的class特化std::swap,并令它调用你的swap函数。
但是,问题,如果我现在是类模板,怎么办?
现在的类是:
template<typename T>
class WidgetImpl{...}
template<typename T>
class Widget{...}
如果你现在这么做:
namespace std{
template<typename T> // 偏特化
void swap<Widget> (Widget<T> & a, Widget<T> & b)
{
a.swap(b) ;
}
}
sorry不行了,原因有以下2点:
1、因为C++只允许对class template进行偏特化,不对能function template进行偏特化。
2、在std这个命名空间,不可以添加新的template到std里。上面,我们能加东西到std是因为,为标准的templates(eg : swap)制造特化版本。
为了解决上述2两个问题,引入下面的解决方法
想法3:转为class template量身打造
定义一个专门的命名空间,放入我们所有的东西
namespace WidgetStuff{
template<typename T>
class WidgetImpl{...}
template<typename T>
class Widget{...} // 这个里面有一个swap函数
template<tyoename T>
void swap(Widget<T>& a , Widget<T>& b)
{
a.swap(b) ;
}
}
我们在用swap函数的时候,应该这么用:
template<typename T>
void doSomething(T& obj1 , T& obj2)
{
using std::swap ; // 这样即使没有T的专属版本的swap函数
swap(obj1 , obj2) ;
}
上面就结束了我们swap函数的制作过程,那么为什么可行呢?这个都是利用了C++的名称查找规则
编译器用“实参取决之查找规则”,找出WidgetStuff中的swap函数,如果命名空间中没有T的专属swap函数,编译器就用std::swap,即使如此,编译器还是喜欢std::swap重T的专属版本,就是想法2会选中。所以,优先级顺序是想法3--》想法2--》标准库自己的swap
总结:
1、弄清楚什么时候我们需要自己写swap函数
2、在class 或者 class template提供一个public swap函数
3、在class 或者 class template的命名空间中提供一个non-member swap ,调用2中的swap
4、如果你正在编写一个class(不是template),为class特化std::swap。并令他调用2中的swap
5、当你实际用swap时,先using std::swap,然后不加任何修饰符,赤裸裸调用swap
成员版swap决不能抛出异常,因为很多异常安全性代码都是通过类的swap成员函数保证的,并且,我们自己去写swap,一般都是用指针,内置类型,这些类型不会抛异常,写swap是为了高效
effective C++ 条款25 swap的更多相关文章
- Effective C++ -----条款25:考虑写出一个不抛异常的swap函数
当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常. 如果你提供一个member swap,也该提供一个non-member swap用来调用前者.对于cla ...
- [More Effective C++]条款22有关返回值优化的验证结果
(这里的验证结果是针对返回值优化的,其实和条款22本身所说的,考虑以操作符复合形式(op=)取代其独身形式(op),关系不大.书生注) 在[More Effective C++]条款22的最后,在返回 ...
- More Effective C++ 条款0,1
More Effective C++ 条款0,1 条款0 关于编译器 不同的编译器支持C++的特性能力不同.有些编译器不支持bool类型,此时可用 enum bool{false, true};枚举类 ...
- Effective C++:条款25:考虑写出一个不抛异常的swap函数
(一) 缺省情况下swap动作可由标准程序库提供的swap算法完毕: namespace std { template<typename T> void swap(T& a, T& ...
- 读书笔记 effective c++ Item 25 实现一个不抛出异常的swap
1. swap如此重要 Swap是一个非常有趣的函数,最初作为STL的一部分来介绍,它已然变成了异常安全编程的中流砥柱(Item 29),也是在拷贝中应对自我赋值的一种普通机制(Item 11).Sw ...
- EC读书笔记系列之13:条款25 考虑写出一个不抛异常的swap函数
记住: ★当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定其不抛出异常 ★若你提供一个member swap,也该提供一个non-member swap来调用前者.对于cla ...
- 条款25:考虑写出一个不抛出异常的swap函数
首先说下标准库的swap算法: namespace std{ template<typename T> void swap(T & a, T & b) { T tmp = ...
- Effective C++ Item 25 考虑写出一个不抛异常的swap函数
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛 ...
- Effective C++ 条款08:别让异常逃离析构函数
1.别让异常逃离析构函数的原因 <Effective C++>第三版中条款08建议不要在析构函数中抛出异常,原因是C++异常机制不能同时处理两个或两个以上的异常.多个异常同时存在的情况下, ...
随机推荐
- Iptables详解+实例
Iptabels是与Linux内核集成的包过滤防火墙系统,几乎所有的linux发行版本都会包含Iptables的功能.如果 Linux 系统连接到因特网或 LAN.服务器或连接 LAN 和因特网的代理 ...
- java项目的部署
1.将tomocat解压到服务器上 2.放项目war包. 3.war包解压. 4.修改端口配置. 1.<Server port="8024" shutdown="S ...
- 说明Oracle数据库逻辑备份和物理备份的方式。
说明Oracle数据库逻辑备份和物理备份的方式. 解答:Oracle备份包括逻辑备份和物理备份. 1).逻辑备份 数据库的逻辑备份包含读一个数据库记录集和将记录集写入文件. a.输出(Export)输 ...
- (转载)【C#4.0】dynamic和var及object
dynamic a = 10;a = a + 10;Console.WriteLine(a.GetType()); 此段代码会输出 System.Int32,第二行不需要类型转换,因为在运行时识别类型 ...
- 2017-2018 ACM-ICPC, NEERC, Northern Subregional Contest D Dividing Marbles
题目大意: 给出一个$N(N <= 2^{22}$),$N$的二进制表示中1的个数不超过4. 一开始有一个集合$S = {N}$, 每次操作可以选择$n\in S \ (n > 1)$, ...
- css中div高度自适应
高度的自适应(父div高度随子div的高度改变而改变) 1.如果父div不定义height.子div均为标准流的时候,父div的height随内容的变化而变化,实现父div高度随子div的高度改变而改 ...
- OpenCV 常用汇总
cv::Rect矩形类用法 typedef struct CvRect { int x; /* 方形的左上角的x-坐标 */ int y; /* 方形的左上角的y-坐标*/ int width; /* ...
- Android显示全局Dialog
1.需求:Dialog只能依附于某个具体的Activity显示,当我们需要一个全局的不依赖于某个Activity的Dialog时,应该怎么创建?类似于QQ消息提醒框 2.分析:我们可以在Service ...
- location.assign 与 location.replace的区别
window.location.assign(url) : 加载 URL 指定的新的 HTML 文档. 就相当于一个链接,跳转到指定的url,当前页面会转为新页面内容,可以点击后退返回上一个页面. w ...
- Win10-64位 免安装版Mysql8下载安装运行
今天忙活了很久去下载安装Mysql,感觉网上的那些教程怎么都对不上呢,很奇怪,不过我乱点一通至少能用了,先凑和着用吧... 记录一下, 要是不对的,以后再修改...windows10系统 2018-5 ...