C++标准库之右值引用与交付语义
C++标准委员会不应该制定一条阻止程序员拿起枪朝自己的脚丫子开火的规则。
右值引用(rvalue)、交付语义(move)
最近阅读《C++标准库第二版》,看到第二章介绍C++11新特性3.1.5节时卡住了。固化的拷贝函数思维,对自认为理所当然的多次拷贝带来的构造和析构的性能瓶颈是否能够优化没有任何想法,给我对于这三个概念带来了很大的阻碍。如果你不曾听说过这三个概念,或和曾经的我一样是执着的"C++ with class"的人的话,我建议你也了解一下这三个概念。C++11并不是印象中的竟是花拳绣腿的功能,这三个带来的编码的清晰和性能的提高可以说是黑科技级别的了。
1 右值引用(rvalue)
1.1 字面意思和语法属性
1.1.1 字面意思
我们先抛开一切,回归到这个概念的字面意思(因为我也被误导了)。
lvalue和rvalue的前缀怎么理解?是否和我一样误认为是left和right这两个英文单词?那就错了。
l表示location,r表示read。
1.1.2 语法和属性
语法:
type && a
请注意:它本身是左值。
1.2 定义
感觉自己理解的还不是特别的透彻,因此不给出以我的话概括出的定义。
关于对右值左值的定义,就百度来的国内文章来说可以说是各有各的说法(当然C++官方对这两概念的定义就有过改动,那使用者就更不用说了)。本人英语比较烂,推荐有能力的话看一下以下文章。
stackoverflow的问答
https://stackoverflow.com/questions/3601602/what-are-rvalues-lvalues-xvalues-glvalues-and-prvalues
msvc给出的定义
https://msdn.microsoft.com/en-us/library/f90831hc.aspx
有空补翻译。
1.3 用法
特点为可以直接引用右值
int && a = 1;
class a {};
a GetA() { return a(); }
a && rref = GetA();
1.4 意义思考
2 交付语义、搬迁语义(move semantic)
以上两词好于“移动语义”这一翻译。
回到性能上的问题,考虑以下swap代码
class A { ... };
void SwapA( A &x, A &y ) {
A tmp( x );
x = y;
y = tmp;
}
你可能认为这无懈可击,但你不可否认在知道交付语义之前这段代码背后隐藏着多复杂的操作而导致了低效率。
光从逻辑带来的代码来看,就有tmp的构造和析构、xy各一次的等于重载符调用。
而这些构造函数、重载符,内有大量的拷贝、内存分配等操作。假设类A是一个字符串函数,那构造和拷贝函数终将为了正确的移动数据,在最坏情况下,他们都不得不把自身原本有的动态内存重新分配、拷贝数据,然后是将临时对象好不容易幸幸苦苦分配的内存给释放掉。
再考虑以下初始化代码
class B {
public:
B( char* str ) {
...
}
};
B x = "ABCDEFG";
同样的,由于这种旧的初始化方法所带来的临时变量,带来的多余内存操作效率极低。
给人的感觉就是乱乱的,无序的,冗余又低效的。而我们的交付语义此时就要以一个能把家里打扫的干干净净的家庭主妇的角色登场了。
请注意:交付语义和拷贝语义相应。
不用太费劲读懂以下代码,他们只是这个世界上不计其数的类的拷贝语义中的一例。是你最常见的拷贝函数。
// copy constructor
A(const A &a) {
m_data = (a.m_data != 0 ?
strcpy(new char[strlen(a.m_data) + 1], a.m_data) : 0);
}
// copy assigment
A &operator =(const A &a) {
if (this != &a) {
delete [] m_data;
m_data = (a.m_data != 0 ? strcpy(new char[strlen(a.m_data) + 1], a.m_data) : 0);
}
return *this;
}
而交付语义可以让你再添加这样的两个函数。
// move constructor
A(A &&a) : m_data(a.m_data) {
a.m_data = 0;
}
// move assigment
A & operator = (A &&a) {
if (this != &a) {
m_data = a.m_data;
a.m_data = 0;
}
return *this;
}
函数参数为一个右值引用,因此需要一个右值才能进行调用。 交付语义或者说搬迁语义由此得名。他实现了我们在洪荒时期的一个小小的遐想,直接将要复制对象的内容托付给复制体。
效率由此提高。若要调用到交付语义的函数,对函数进行如下修改。
void SwapA( A &x, A &y ) {
A tmp( std::move(x) );
x = std::move(y);
y = std::move(tmp);
}
// std::move( T )将一个左值变为右值。意味着如果有提供move的话使用move语义,否则使用copy。
而本身为右值的字符串的初始化代码不用改动。
class B {
public:
B( char* str ) {
...
}
};
B x = "ABCDEFG"; // 只调用了一次new。临时对象的内存地址直接交付给了B。
安全爽快地释放掉原来的内存,将右值有用的那部分直接拿过来,这就是交付语义的办事理念。
不仅是拷贝行为和构造行为可以应用这一特性,如vector的pushback,字符串的setstr也可以使用,这些函数行为与构造和拷贝类似。
C++标准库之右值引用与交付语义的更多相关文章
- 【转】C++11 标准新特性: 右值引用与转移语义
VS2013出来了,对于C++来说,最大的改变莫过于对于C++11新特性的支持,在网上搜了一下C++11的介绍,发现这篇文章非常不错,分享给大家同时自己作为存档. 原文地址:http://www.ib ...
- C++11 标准新特性: 右值引用与转移语义
文章出处:https://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/ 新特性的目的 右值引用 (Rvalue Referene) ...
- C++标准库之右值引用相关:引用折叠
引用折叠 引用折叠出现的情况在于范型编程时. void f(T&& param); f(10); int x = 10; f(x); 这两者都可运行成功. 由于存在T&& ...
- C++11 右值引用和转移语义
新特性的目的 右值引用 (Rvalue Referene) 是 C++ 新标准 (C++11, 11 代表 2011 年 ) 中引入的新特性 , 它实现了转移语义 (Move Sementics) 和 ...
- C++11 右值引用 与 转移语义
新特性的目的 右值引用(R-value Reference)是C++新标准(C++11, 11代表2011年)中引入的新特性,它实现了转移语义(Move Semantics)和精确传递(Perfect ...
- [c++11]右值引用、移动语义和完美转发
c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...
- [转][c++11]我理解的右值引用、移动语义和完美转发
c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...
- 关于C++11右值引用和移动语义的探究
关于C++11右值引用和移动语义的探究
- C++11新特性之右值引用(&&)、移动语义(move)、完美转换(forward)
1. 右值引用 个人认为右值引用的目的主要是为了是减少内存拷贝,优化性能. 比如下面的代码: String Fun() { String str = "hello world"; ...
随机推荐
- 使用Java监控工具出现 Can't attach to the process
问题重现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ➜ jinfo -flags 3032 Attaching ...
- 如何查看k8s存在etcd中的数据(转)
原文 https://yq.aliyun.com/articles/561888 一直有这个冲动, 想知道kubernetes往etcd里放了哪些数据,是如何组织的. 能看到,才有把握知道它的实现和细 ...
- vue打包
npm installnpm run devnpm run build @types/node npm install --save @types/node npm install @types/no ...
- SpringMVC的工作流程、组件说明以及常用注解说明
1. SpringMVC处理流程 2. SpringMVC架构 2.1 框架结构 2.2 框架流程 1. 用户发送请求至前端控制器DispatcherServlet. 2. DispatcherSer ...
- issue_hana
2019-01-11T14:35:01.910187+08:00 SHADEVDB01 cron[57062]: PAM unable to dlopen(/lib64/security/pam_sy ...
- RedHat 7.0更新升级openSSH7.4p1
由于目前服务器上ssh版本较低,存在安全漏洞,需要升级到最新版本. 系统版本:RedHat 7.0 旧openSSH版本:6.4p1 新openSSH版本:7.4p1 升级方式:源码安装 安装操作步骤 ...
- Python-反射getattr的应用
login.py #!/usr/bin/dev python# coding:utf-8 def index(): print u'欢迎访问xx网站首页' def login(): print u'登 ...
- nexus的安装和简介
下载nexus Nexus 是Maven仓库管理器,通过nexus可以搭建maven仓库,同时nexus还提供强大的仓库管理功能,构件搜索功能等. 下载Nexus, 下载地址:http://www.s ...
- linux下启动tomcat服务
Linux下tomcat服务的启动.关闭与错误跟踪,使用PuTTy远程连接到服务器以后,通常通过以下几种方式启动关闭tomcat服务:切换到tomcat主目录下的bin目录(cd usr/local ...
- tfs填坑那些事
1.csdn下载 安装(步1:装 步2:激活 步3:配置数据库之类 选择完全配置)激活(win7能出来,win10不出来) 注意 tfs服务开启,,代理服务开启 2.选择敏捷模板 3.无法新建项目, ...