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) ...
随机推荐
- H 模拟水题
n个人 小明编号为m 从编号为a的人开始数 起始数字为b 遇到素数duang 并反相 求小明应该说什么 Sample Input 310 2 //n m3 4 //a b3 32 64 13 8 Sa ...
- hdu3261
题意: 求至少出现k次的最长字符串(可重复) 题解: 后缀数组维护height数组只要找连续k个的最小值 代码: #include <cstdio> #include <algori ...
- java:矩阵面积
实现一个矩阵类Rectangle,包含如下的一些成员变量与函数: 两个共有的成员变量 width 和 height 分别代表宽度和高度. 一个构造函数,接受2个参数 width 和 height 来设 ...
- 【AtCoder】Tenka1 Programmer Contest
C - 4/N 列出个方程枚举解一下 #include <bits/stdc++.h> #define fi first #define se second #define pii pai ...
- python函数式编程——匿名函数(lambda)
匿名函数lambda lambda x:x*x x就是参数 相当于函数 def f(x): return x*x 匿名函数可以作为函数对象赋值给变量: >>> f = lambda ...
- Codeforces Round #437 (Div. 2, based on MemSQL Start[c]UP 3.0 - Round 2)
Problem A Between the Offices 水题,水一水. #include<bits/stdc++.h> using namespace std; int n; ]; i ...
- BZOJ1965 [Ahoi2005]SHUFFLE 洗牌 快速幂
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1965 题意概括 对于扑克牌的一次洗牌是这样定义的,将一叠N(N为偶数)张扑克牌平均分成上下两叠,取 ...
- 6.Django与Ajax
Ajax 文件夹为Ajaxdemo 向服务器发送请求的途径: 1.浏览器地址栏,默认get请求: 2.form表单: get请求 post请求 3.a标签,超链接(get请求) 4.Ajax请求 特点 ...
- Python4 - 文件操作
对文件操作流程 打开文件,得到文件句柄并赋值给一个变量 文件的内存对象-包含 文件名.字符集.大小.在硬盘上的起止位置... 通过句柄对文件进行操作 关闭文件 open 方法 open()函数打开一个 ...
- Spring根据包名获取包路径下的所有类
参考mybatis MapperScannerConfigurer.java 最终找到 Spring的一个类 ClassPathBeanDefinitionScanner.java 参考ClassP ...