C++11的右值引用的具体使用
C++11引入了std::move语义、右值引用、移动构造和完美转发这些特性。由于这部分篇幅比较长,分为3篇来进行阐述。
在了解这些特性之前,我们先来引入一些问题。
一、问题导入
- 函数返回值是传值的时候发生几次对象构造、几次拷贝?
- 函数的形参是值传递的时候发生几次对象构造?
让我们先来看一段代码,
//main.cpp #includeusingnamespacestd; classA{ public: A(){ cout<<"classAconstruct!"< 使用g++编译;注意使用-fno-elide-constructors关闭省略构造优化
g++main.cpp-fno-elide-constructors可以得到以下输出
classAconstruct!
classAdestruct!
classAcopy!
classAdestruct!
classAdestruct!
可以看到Aa=get_A_value();一行代码居然产生1次对象构造和2次对象的拷贝构造!具体为
- 在get_A_value()里A()构造了临时对象,发生了一次构造;
- 函数返回的时候会把临时对象拷贝后作为返回值,发生一次拷贝;
- Aa=函数返回值发生了拷贝构造。
如果使用编译器优化(默认),则会把临时对象拷贝的那次和用返回值构造最终对象的拷贝的给省略了;也即,只有一次拷贝和析构。
classAconstruct!
classAdestruct!
如果把上面代码改一下
//...A voidpass_A_by_value(Aa){ } intmain(){ Aa; pass_A_by_value(a); return0; }在去掉优化g++main.cpp-fno-elide-constructors时输出为
classAconstruct!
classAcopy!
classAdestruct!
classAdestruct!
1次构造加上1次拷贝。
因此,下次如果面试的时候有人问这个问题,你就可以说:默认情况下经过编译器优化后临时对象的拷贝就会被省去,如果使用-fno-elide-constructors省略优化,则还要考虑临时对象的拷贝。
事实上,在未经优化的情况下,以下时候拷贝构造函数会被调用:
- 函数内的局部对象做为返回值返回(不是引用)的时候会发生拷贝(拷贝为临时对象返回)
- 函数形参为传值的时候,会发生拷贝构造
- 一个对象以另外一个对象进行初始化的时候
对象的频繁构造是程序的开销,特别是当对象内部有堆上内存(比如有new出来的成员)的时候,每次拷贝构造的时候都需要用new申请一块内存,造成性能的降低。对于情况2,好习惯是如果函数参数是只读的(也即不会在程序内进行修改),传引用作为参数,也即pass_A_by_refrence(constA&a);对于情况1,编译器会为我们进行优化;对于情况3,C++11引入了一种移动构造函数的概念,它将获取**右值引用*,右值的“资源”move到新对象中,这个过程中不会申请新的内存,从而达到提高了效率和性能。
所以,要理解些关键词“移动构造”、“移动语义”,首先要理解右值和右值引用。
二、右值和右值引用
2.1左值(lvalue)和右值(rvalue)
在一、问题导入里我们提到了临时对象,也即函数返回值的时候只会“临时”存在的对象(运行超过那一行就会结束它的生存期),这个临时返回值就是一个右值;
右值的最直观的定义为,顾名思义:位于赋值运算符=右边的值,为右值;在左边的则为左值
如
Aa=foo();//foo()为右值 char*x="thu";//“thu”为字面值也为右值 a=b+c;//b+c这个结果也是一个右值在C++中,还有个定义为:
左值可以取得地址、有名字;不可以取得地址、没有名字的为右值。
所以Aa=foo()可以用&a取得a的地址,a是左值,然是不能取得foo()的地址,(&foo())无法通过编译,foo()返回的临时对象也是没有名字的,所以是右值。
在C++11中,右值包括两种,一中是将亡值(xvalue,eXpiringValue),一种是纯右值(prvalue,PureRvalue)[1]。函数非引用返回的临时对象、运算表达式的结果、1,3.14,'c'这样的字面值等都属于纯右值。而xvalue则是由C++11引入的如返回值为A&&的函数返回值或者std::move()的返回值等。
不深究的话,我们只需要知道左值和右值的区别就行了。对于右值的详细分类则不必深究。
2.2左值引用和右值引用
左值引用就是一般的引用,一般用一个&表示,例如
constA&a_ref=a;//取得对象a的引用左值引用相当于别名,指向一个具体的对象。
右值引用
右值引用顾名思义,就是右值的引用,用&&表示;
A&&r_ref=getRvalue();//r_ref是一个右值引用
右值引用也相当于别名,与左值的区别为右值引用是无名变量的别名。getRvalue()是一个返回右值的函数,右值在这一句执行完就该结束他的生存期了,如果是对象就该调用析构函数了;但是==右值引用让它强行续命==;使用右值引用指向右值,右值的生存期和右值引用一样长了,这也就少一次对象的析构和构造了。
C++的右值引用主要有两个用处,一个是移动语义,一个是完美转发。这个将在接下来的两篇来讲。
总结
为了导入右值和移动语义,首先复习了以下临时对象在函数返回值和传参数时构造了几次;然后对比介绍了左值和右值,以及右值引用的形式和含义。为移动语义和完美转发的介绍做铺垫。
参考资料
MichaleWang|IBMXL编译器中国《深入理解C++11》,机械工业出版社
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。