swap function & copy-and-swap idiom
在C++中,众所周知在一个资源管理类(例如含有指向堆内存的指针)中需要重新定义拷贝构造函数、赋值运算符以及析构函数(Big Three),在新标准下还可能需要定义移动构造函数和移动赋值运算符(Big Five)。但实际上,这条规则还可以有一个小扩展。就是在资源管理类中,往往需要重新定义自己的swap函数来作为优化手段。
1. swap函数
首先考察如下例子,假设类HasPtr中含有一个指向string的指针 *ps 和一个int类型值value。
class HasPtr {
public:
...
...
...
private:
string* ps;
int value;
};
则如果没有定义自己的swap函数的话,调用标准库的std::swap相当于进行如下动作:
HasPtr temp = v1;
v1 = v2;
v2 = temp;
这里v1中的string拷贝了两次,造成了不必要的效率上的浪费。
其实我们知道,这里swap只需交换指针指向就可以,所以我们可以写出如下自定义版本的swap函数:
class HasPtr {
friend void swap(HasPtr& lhs, HasPtr& rhs);
...
...
}; inline
void swap(HasPtr& lhs, HasPtr& rhs) {
using std::swap;
swap(lhs.ps, rhs,ps);
swap(lhs.value, rhs.value);
}
注意: 这里有一点值得注意的是,函数调用的时候应该写成swap,而非std::swap形式。这是因为假如有另一个类型Foo含有成员h(HasPtr类型),则如果HasPtr定义了自己的swap函数的话,应该调用其自定义的swap函数,否则调用标准库的std::swap。
所以如下形式1是不对的,应该写成形式2所示。
二者执行结果相同,但效率不同(前者不必要的拷贝)
//形式1
void swap(Foo& lhs, Foo& rhs) {
std::swap(lhs.h, rhs.h);
}
//形式2
void swap(Foo & lhs, Foo & rhs) {
using std::swap;
swap(lhs.h, rhs.h);
}
2. copy and swap idiom
定义了swap函数的类常用swap来定义他们的赋值运算符,即copy and swap技术。
这样做可以做到天然的异常安全并且正确处理自我赋值问题,是简洁高效安全的写法。(为什么其他方法存在异常安全或自我赋值问题可以参加effective c++条款11)
上述例子使用copy and swap技术定义赋值运算符如下:
HasPtr& HasPtr::operator=(HasPtr rhs) {
swap(*this, rhs);
return *this;
}
可以看出,不同于一般的赋值运算符,其参数采用传值方式,而非引用。它与如下代码功能上等价,但效率上更高(将copy动作移至函数参数构造阶段)
HasPtr& HasPtr::operator=(const HasPtr& rhs) {
HasPtr temp(rhs);
swap(*this, temp);
return *this;
}
这两种方式均可以完美解决异常安全和自我赋值问题。第一种方法被C++ primer和C++编程规范所推荐,Scott Meyers在effective c++中表达了一些对其可读性的忧虑,但如果掌握好copy and swap idiom,还是应该推荐采用第一种写法生成更高效的代码。
参考资料:
1.Stanley B. Lippman / Josée Lajoie / Barbara E. Moo, C++ Primer 中文版(第 5 版)[M]. 电子工业出版社,2013
2.Meyers S. Effective C++[M]. Addison Wesley, 2005.
3.Sutter H, Alexandrescu A. C++ Coding Standards: 101 Rules, Guidelines, and Best Practices (C++ in Depth Series)[M]. Addison-Wesley Professional, 2004.
4.关于swap and copy idiom问题在stackoverflow上的解答: http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
//形式1
void swap(Foo& lhs, Foo& rhs) {
std::swap(lhs.h, rhs.h);
}
//形式2
void swap(Foo & lhs, Foo & rhs) {
using std::swap;
swap(lhs.h, rhs.h);
}
swap function & copy-and-swap idiom的更多相关文章
- C++异常安全、copy and swap
异常安全的代码是指,满足两个条件 1异常中立性 : 是指当你的代码(包括你调用的代码)引发异常时,这个异常 能保持原样传递到外层调用代码.(异常中立,就是指任何底层的异常都会抛出到上层,也就相当于是异 ...
- 做个地道的c++程序猿:copy and swap惯用法
如果你对外语感兴趣,那肯定听过"idiom"这个词.牛津词典对于它的解释叫惯用语,再精简一些可以叫"成语".想要掌握一门语言,其中的"成语" ...
- c++异常安全和copy and swap策略
异常安全有两个目标: 不泄露任何资源.这个通过RAII可以做到. 不破坏数据结构.这是下文要讨论的事情 异常安全有三个级别: 基本安全:异常发生后对象和数据结构还有合法状态.实现简单,应该作为最低要求 ...
- copy and swap技巧与移动赋值操作符
最近在实现一个Delegate类的时候碰到了一个问题,就是copy and swap技巧和移动赋值操作符有冲突. 比如有以下一个类: class Fun { public: Fun(const Fun ...
- wubi安装ubuntu后,增加swap大小,优化swap的使用参数-----------让ubuntu健步如飞,为编译android源码准备
wubi安装ubuntu后,终端输入free -m可以查到如下信息: total used free shared buffers cached Mem: 1944 1801 143 0 557 70 ...
- linux swap 分区调控(swap分区 lvm管理)
注:linux swap分区 采用lvm管理,调控可以采用下面的方法 一.查看 swap lv [root@testdb ~]# vgdisplay -v Finding all volume ...
- Ubuntu 16.04创建Swap分区或增加Swap分区容量(转)
要在Ubuntu中要创建Swap分区主要有如下2种方式: 一.传统创建方式 一般情况下,我们都会使用dd命令来预先创建交换分区文件,然后再用/dev/zero将该文件的内容全部置零,创建时还将用到bs ...
- 剖析std::function接口与实现
目录 前言 一.std::function的原理与接口 1.1 std::function是函数包装器 1.2 C++注重运行时效率 1.3 用函数指针实现多态 1.4 std::function的接 ...
- C++ Core Guidelines
C++ Core Guidelines September 9, 2015 Editors: Bjarne Stroustrup Herb Sutter This document is a very ...
随机推荐
- Leetcode173. Binary Search Tree Iterator二叉搜索树迭代器
实现一个二叉搜索树迭代器.你将使用二叉搜索树的根节点初始化迭代器. 调用 next() 将返回二叉搜索树中的下一个最小的数. 注意: next() 和hasNext() 操作的时间复杂度是O(1),并 ...
- LUOGU P3052 [USACO12MAR]摩天大楼里的奶牛Cows in a Skyscraper
题目描述 A little known fact about Bessie and friends is that they love stair climbing races. A better k ...
- web前端学习(四)JavaScript学习笔记部分(9)-- JavaScript面向对象详解
1.认识面向对象 1.1.概念 1.一切事物皆是对象 2.对象具有封装和继承特性 3.信息隐藏(类的信息隐藏,包括属性和方法) <!DOCTYPE html> <html lang= ...
- DynamicDataDisplay 双击获取坐标
近日由于项目需要,学习了DynamicDataDisplay实现动态曲线图,网上的资料基本上够用了,就是双击获得数据点没能找到资料,只好下载了DynamicDataDisplay的源码来学习.总结共享 ...
- 对BOM的总结
参考:JavaScript半知半解 TG著 BOM对象 Window对象是客户端JavaScript程序的全局对象. Window对象使得JavaScript与浏览器进行交互. 所有的JavaScri ...
- VMware 安装 ubuntu 后安装 VMWare tools
1.如果 VMware 的安装 VMWare tools 的菜单是灰色, 很可能原因是: 你的 cdrom 被占用着. 关闭系统, 编辑配置, 把cdrom 改为 自动检测. 即不要开始就加载一 ...
- Kubernetes 基于 ubuntu18.04 手工部署 (k8s)
由于工作的需要, 手工部署一个 Kubernetes 环境(k8s).(以前都是云上搞定,拿来用) 习惯把这种工作记录下来,自己备查也和别人分享 网上相关文章很多, 我也参考了很多,这里推荐一个 链接 ...
- Jeecms网站直接访问html静态页面
jeecms网站维护,遇到了直接通过链接的方式访问静态页面,jeecms官网也做了详细的解答,但是没有得到满意的结果.但是通过自己的深入研究以及别人的帮助,发现了一个很好的解决方法. 首先说明一下je ...
- LintCode刷题笔记-- LongestCommonSquence
标签:动态规划 题目描述: Given two strings, find the longest common subsequence (LCS). Your code should return ...
- cmd操作oracle
利用cmd的操作命令导出,详情如下(备注:方法二是转载网上的教程):1:G:\Oracle\product\10.1.0\Client_1\NETWORK\ADMIN目录下有个tnsname.ora文 ...