C++11中的右值引用及move语义编程
C++0x中加入了右值引用,和move函数。右值引用出现之前我们只能用const引用来关联临时对象(右值)(造孽的VS可以用非const引用关联临时对象,请忽略VS),所以我们不能修临时对象的内容,右值引用的出现就让我们可以取得临时对象的控制权,终于可以修改临时对象了!而且书上说配合move函数,可以大大提高现有C++的效率。那么是怎样提高它的效率的呢?看段代码先!
#include <iostream>
#include <utility>
#include <vector>
#include <string>
int main()
{
std::string str = "Hello";
std::vector<std::string> v; // uses the push_back(const T&) overload, which means
// we'll incur the cost of copying str
v.push_back(str);
std::cout << "After copy, str is \"" << str << "\"\n"; // uses the rvalue reference push_back(T&&) overload,
// which means no strings will copied; instead, the contents
// of str will be moved into the vector. This is less
// expensive, but also means str might now be empty.
v.push_back(std::move(str)); //注意: void push_back( T&& value );
std::cout << "After move, str is \"" << str << "\"\n"; std::cout << "The contents of the vector are \"" << v[]
<< "\", \"" << v[] << "\"\n";
} 结果:
After copy, str is "Hello"
After move, str is ""
The contents of the vector are "Hello", "Hello"
看完大概明白一点儿了,加上move之后,str对象里面的内容被"移动"到新的对象中并插入到数组之中了,同时str被清空了。这样一来省去了对象拷贝的过程。所以说在str对象不再使用的情况下,这种做法的效率更高一些!但问题是str的内容在什么地方被移走的呢?move函数到底是干啥的?扣一下stl源码吧,下面是move模板的源码:
// TEMPLATE FUNCTION move
template<class _Ty> inline
typename tr1::_Remove_reference<_Ty>::_Type&&
move(_Ty&& _Arg)
{ // forward _Arg as movable
return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg); //从这里到返回值的时候,发生了string的移动拷贝构造函数调用,故字符串转移到了右值引用变量中去了。
}
好吧,看过了这段,可能有人又迷惑了,不是说有名左指变量不能绑定到右值引用上面么?为什么move函数的参数是右值引用却可以接受左值变量作为参数?难道STL错了么?事实上,C++0x在引入右值引用的时候对函数模板自动推导也加入了新的规则,简单的说,像例子中的这种情况,模板参数是_Ty而函数的参数是_Ty&&(右值引用),同时_Arg是string的左值对象的情况下,会触发一个特殊规则,_Ty会推导成string&,也就是说此事推导出来的函数与move<string&>一致。那么move(_Ty&& _Arg) 得到的应该是move(string& && _Arg)这个时候根据引用折叠原则,会变成这个样子move(string& _Arg)。详细的描述参见白云飘飘翻译的vc技术文档(http://www.cppblog.com/kesalin/archive/2009/06/05/86851.html)。函数的返回值嘛,就好说了,就是返回所持有类型的右值引用了。所以,move函数的作用很简单,不管你给什么参数,都返回对应类型的右值引用!那么,上面例子中str的不是在move函数中被移走的。综上,我们猜测str内容肯定是在构造新对象的过程中被新对象偷走的,也就是在string的参数为右值引用的构造函数中被偷走的!翻看string的源码(来自VS实现的STL),果然如此啊!如下:
basic_string(_Myt&& _Right)
: _Mybase(_STD forward<_Alloc>(_Right._Alval))
{ // construct by moving _Right
_Tidy();
assign(_STD forward<_Myt>(_Right));
} _Myt& assign(_Myt&& _Right)
{ // assign by moving _Right
if (this == &_Right)
;
else if (get_allocator() != _Right.get_allocator()
&& this->_BUF_SIZE <= _Right._Myres)
*this = _Right;
else
{ // not same, clear this and steal from _Right
_Tidy(true);
if (_Right._Myres < this->_BUF_SIZE)
_Traits::move(this->_Bx._Buf, _Right._Bx._Buf,
_Right._Mysize + );
else
{ // copy pointer
this->_Bx._Ptr = _Right._Bx._Ptr;
_Right._Bx._Ptr = ;
}
this->_Mysize = _Right._Mysize;
this->_Myres = _Right._Myres; _Right._Tidy();
}
return (*this);
}
所以,我们知道了,C++0x在STL模板库中加入了参数为右值引用的构造函数,用于把参数所关联对象中的数据移动到新对象当中,避免了深度拷贝,增加了效率。再详细翻看源码,可以发现除了构造函数,operator=也重载了一个参数为右值引用的函数,用途和构造函数类似。所以我们自定义中的类也应该增加参数为右值引用的构造函数和重载赋值运算符!原因是啥,看例子!
未定义参数为右值引用的构造函数:
#include <iostream>
#include <utility>
#include <vector>
#include <string> using namespace std; class MyPoint{
public:
MyPoint()
:comment(""), x(), y()
{
} MyPoint(const MyPoint& p)
:comment(p.comment),x(p.x),y(p.y)
{} //MyPoint(MyPoint&& p)
// :comment(move(p.comment)), x(p.x), y(p.y)
//{
// p.x = 0;
// p.y = 0;
//} string toString()
{
char buf[];
sprintf(buf, "%s: %d %d", comment.c_str(), x, y); return buf;
} string comment;
int x;
int y; }; int main()
{
MyPoint p;
p.comment = "First point";
p.x = ;
p.y = ; vector<MyPoint> v; v.push_back(p); cout << "After copy, str is \"" << p.toString() << "\"\n"; v.push_back(move(p));
cout << "After move, str is \"" << p.toString() << "\"\n"; cout << "The contents of the vector are \"" << v[].toString()
<< "\", \"" << v[].toString() << "\"\n"; cin.get();
} 结果:
After copy, str is "First point: 9 7"
After move, str is "First point: 9 7"
The contents of the vector are "First point: 9 7", "First point: 9 7" 定义了参数为右值引用的构造函数之后:
After copy, str is "First point: 9 7"
After move, str is ": 0 0"
The contents of the vector are "First point: 9 7", "First point: 9 7"
综上所述,C++0x中的move语义编程,不仅仅是在应用的时候使用参数中加上move,对于自定义类需要增加参数为右值引用的构造函数和赋值运算符,这种构造函数我们称为move构造函数!
C++11中的右值引用及move语义编程的更多相关文章
- C++ 11 中的右值引用
C++ 11 中的右值引用 右值引用的功能 首先,我并不介绍什么是右值引用,而是以一个例子里来介绍一下右值引用的功能: #include <iostream> #include &l ...
- [转载] C++11中的右值引用
C++11中的右值引用 May 18, 2015 移动构造函数 C++98中的左值和右值 C++11右值引用和移动语义 强制移动语义std::move() 右值引用和右值的关系 完美转发 引用折叠推导 ...
- C++ 11中的右值引用以及std::move
看了很多篇文章,现在终于搞懂了C++ 中的右值以及std::move 左值和右值最重要的区别就是右值其实是一个临时的变量 在C++ 11中,也为右值引用增加了新语法,即&& 比 ...
- C++11中的右值引用
原文出处:http://kuring.me/post/cpp11_right_reference May 18, 2015 移动构造函数 C++98中的左值和右值 C++11右值引用和移动语义 强制移 ...
- 【转载】C++ 11中的右值引用
本篇随笔为转载,原博地址如下:http://www.cnblogs.com/TianFang/archive/2013/01/26/2878356.html 右值引用的功能 首先,我并不介绍什么是右值 ...
- [c++11]右值引用、移动语义和完美转发
c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的 ...
- [转][c++11]我理解的右值引用、移动语义和完美转发
c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 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) ...
随机推荐
- dll反编译工具总结
有好多.net程序有加密狗或者有验证,如果exe或dll没有做过特殊处理,破解.net程序其实很简单,不过你要有足够的耐心! 我只做个简单的小例子,把公司的软件破解了,不要被老大知道,吼吼~~~~ 1 ...
- BZOJ5074 小B的数字 BZOJ2017年10月月赛 其他
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ5074 题意概括 题解 作为蒟蒻的我第一个就选择了过的人最多的D题. 不仔细看好吓人. 然而并不难. ...
- Nginx 部署、反向代理配置、负载均衡
Nginx 部署.反向代理配置.负载均衡 最近我们的angular项目部署,我们采用的的是Nginx,下面对Nginx做一个简单的介绍. 为什么选择Nginx 轻:相比于Apache,同样的web服务 ...
- 618大促微服务、web、redis等的超时时间
1. 最近因为大促原因线上服务不稳定,不稳定主要是redis经常超时并且数据为定时mGet方式获得 节点一多,所有服务节点同时获取数据访问量变大导致get取数据变慢因mGet会对数据进行锁住操作, 此 ...
- 10.24 正睿停课训练 Day8 AM
目录 2018.10.24 正睿停课训练 Day8 AM A 棒棒糖(组合) B 彩虹糖(思路 博弈) C 泡泡糖(DP) 考试代码 A B C 2018.10.24 正睿停课训练 Day8 AM 期 ...
- 洛谷.1501.[国家集训队]Tree II(LCT)
题目链接 日常zz被define里没取模坑 //标记下放同线段树 注意51061^2 > 2147483647,要开unsigned int //*sz[]别忘了.. #include < ...
- Python3练习题系列(02)
题目: 思考循环结构,看看它是怎样运行的,对我们认识程序有何益处. 知识点: list, for-loop, range 练习代码: 练习1 the_count = [1, 2, 3, 4, 5] # ...
- Flask信号源码流程
1. appcontext_pushed = _signals.signal('appcontext-pushed'# 请求app上下文push时执行 return RequestContext(se ...
- GC日志
JVM的GC日志的主要参数包括如下几个: -XX:+PrintGC 输出GC日志 -XX:+PrintGCDetails 输出GC的详细日志 -XX:+PrintGCTimeStamps 输出GC的时 ...
- 20172302 《Java软件结构与数据结构》第三周学习总结
2018年学习总结博客总目录:第一周 第二周 第三周 教材学习内容总结 第五章 队列 1.队列是一种线性集合,其元素从一端加入,从另一端删除:队列元素是按先进先出(FIFO(First in Firs ...