c++ 右值引用,move关键字
c++ move关键字
move的由来:在 c++11 以前存在一个有趣的现象:T& 指向 lvalue (左传引用), const T& 既可以指向 lvalue 也可以指向 rvalue。但却没有一种引用类型,可以限制为只指向 rvalue。
c++11 中的 move() 是这样一个函数,它接受一个参数,然后返回一个该参数对应的右值引用.
就这么简单!你甚至可以暂时想像它的原型是这样的(当然是错的)
T&& move(T& val);
&&的由来:在函数体中,程序员无法分辨传进来的参数到底是不是 rvalue,我们缺少一个 rvalue 的标记。为了解决这个问题,c++11 中引入了一个新的引用类型: some_type_t &&,这种引用指向的变量是个 rvalue。
由于&和&&是属于不同的类型,所以用于各种函数(构造函数,赋值函数)的重载
holder(holder& other)
holder(holder&& other)
上面是2个重载函数
holder& operator=(holder& other)
holder& operator=(holder&& other)
上面是2个重载函数
具体看下面的例子:假设我们有一个类,它包含了一些资源。我们的愿望是:当调用拷贝构造函数或者赋值语句时,我们不想再new一个Resource的对象,因为要new一个Resource对象,即浪费空间,有浪费时间。
解决办法:利用右值引用。
右值,本质上是一个临时的内存空间,使用过后,系统马上就会释放掉它,有了右值引用后,就可以延长这个临时空间的生命周期,相当于有了左值的效果,所以可以使用这个临时空间了。回到上面提出的问题,我们不想再new一个Resource的对象,这时我们就可以利用右值引用,去引用一个临时的并且马上要被释放的空间。
为了调用&&的拷贝构造函数,必须使用std::move,转化成右值引用.
holder h2(std::move(get_holer()))
但是,h1 = get_holer(),即使不使用std::move,也会调用&&的赋值函数
h1 = get_holer()
完整代码:
#include <iostream>
using namespace std;
class Resource{};
class holder{
public:
//构造函数
holder(){res = new Resource();}
//析构函数
~holder(){
//res不为NULL,就释放res
if (res)delete res;
}
//拷贝构造函数
holder(const holder& other){
cout << "holder&" << endl;
res = new Resource(*other.res);
}
holder(holder& other){
cout << "holder&1" << endl;
res = new Resource(*other.res);
}
//右值
holder(holder&& other){
cout << "holder&&" << endl;
res = other.res;
other.res = nullptr;
}
//赋值
holder& operator=(const holder& other){
cout << "operator" << endl;
delete res;
res = new Resource(*other.res);
return *this;
}
holder& operator=(holder& other){
cout << "operator1" << endl;
delete res;
res = new Resource(*other.res);
return *this;
}
//右值
holder& operator=(holder&& other){
cout << "operator &&" << endl;
std::swap(res, other.res);
return *this;
}
private:
Resource* res;
};
holder get_holer(){
holder h;
return h;
}
int main(void){
holder h1,h11;
holder h2(std::move(get_holer()));//调用holder(holder&& other)
holder h3(get_holer());//编译器自动优化了,没有调用拷贝构造函数
holder h4(h11);
h1 = h2;//调用operator(holder&);
h1 = get_holer();//调用operator(holder&&);
}
上面的例子有个需要注意的地方,就是下面这行代码
holder h3(get_holer());//编译器自动优化了,没有调用拷贝构造函数
这行代码乍一看,应该调用拷贝构造函数。但是实际用GDB,断点调试时,发现并没有调用拷贝构造函数。
个人的猜测:如果编译器不优化,在函数get_holer()的return一行处就应该有一次拷贝,调用拷贝构造函数,把h拷贝一份返回给调用测,然后由于holder h3(get_holer())的写法,又要调用一次拷贝构造函数,把get_holer()返回值拷贝给h3。这样一来就多了2次不必要的拷贝,所以编译器自动优化,把这2次拷贝构造函数的调用都省略掉了,直接让h3 = 在get_holer()创建的对象
65 holder h2(std::move(get_holer()));
(gdb) n
holder&&
66 holder h3(get_holer());//编译器自动优化了,没有调用拷贝构造函数
(gdb) s
get_holer () at rvalue_move.cpp:59
59 holder h;//调用一次构造函数,在下面的9行可以看到
(gdb) s
holder::holder (this=0x7fffffffe180) at rvalue_move.cpp:9
9 holder(){res = new Resource();} //调用构造函数,创建h对象
(gdb) s
get_holer () at rvalue_move.cpp:60
60 return h; //返回h对象
(gdb) p h //查看h对象里面res的内存地址
$12 = {res = 0x603070}
(gdb) p &h //查看h的内存地址
$13 = (holder *) 0x7fffffffe180
(gdb) n
61 }
(gdb) n
main () at rvalue_move.cpp:67
(gdb) p h3 //查看h3对象里面res的内存地址,发现h3的res的内存地址和在函数get_holer()里创建的h对象的res的内存地址相同
$14 = {res = 0x603070}
(gdb) p &h3 //查看h3的内存地址后,发现h3的内存地址和在函数get_holer()里创建的h对象的内存地址相同
$15 = (holder *) 0x7fffffffe180
c++ 右值引用,move关键字的更多相关文章
- C++11右值引用和std::move语句实例解析
关键字:C++11,右值引用,rvalue,std::move,VS 2015 OS:Windows 10 右值引用(及其支持的Move语意和完美转发)是C++0x将要加入的最重大语言特性之一.从实践 ...
- c/c++ 右值引用,forward关键字
c++ forward关键字 forward的由来:模板函数中的推导类型,作为另一函数的参数时,不管实参是什么类型,作为另一个参数的实参时,都变成了左值.因为C++里规定函数的形参就是左值,不过调用侧 ...
- 第15课 右值引用(2)_std::move和移动语义
1. std::move (1)std::move的原型 template<typename T> typename remove_reference<T>::type& ...
- std::move的原理与实现,右值引用的深入理解
这次我真的懂了.... 首先C++11引入了右值引用 && ‘&&’这个要连起来看,是一个整体,C++多了一个关键字而已. 不是引用的引用.是船新的一种语法.那有什么用 ...
- C++ 右值引用与move
C++ 右值引用与move 右值引用 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的持久化对象,右值是指表达式结束时就不再存在的临时对象. 所有的具名变量或者对象都是左值 ...
- 右值引用、move与move constructor
http://blog.chinaunix.net/uid-20726254-id-3486721.htm 这个绝对是新增的top特性,篇幅非常多.看着就有点费劲,总结更费劲. 原来的标准当中,参数与 ...
- move语义和右值引用
C++11支持move语义,用以避免非必要拷贝和临时对象. 具体内容见收藏中的“C++右值引用” .
- C++ 11中的右值引用以及std::move
看了很多篇文章,现在终于搞懂了C++ 中的右值以及std::move 左值和右值最重要的区别就是右值其实是一个临时的变量 在C++ 11中,也为右值引用增加了新语法,即&& 比 ...
- 翻译「C++ Rvalue References Explained」C++右值引用详解 Part6:Move语义和编译器优化
本文为第六部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...
随机推荐
- linux:终端常用命令 + vi命令修改文件及保存 方法
首先介绍一下Ubuntu下各个目录的一般作用: / 这就是根目录,一台电脑有且只有一个根目录,所有的文件都是从这里开始的.举个例子:当你在终端里输入“/home”,你其实是在告诉电脑,先从/(根目录 ...
- [机器学习]回归--Support Vector Regression(SVR)
来计算其损失. 而支持向量回归则认为只要f(x)与y偏离程度不要太大,既可以认为预测正确,不用计算损失,具体的,就是设置阈值α,只计算|f(x)−y|>α的数据点的loss,如下图所示,阴影部分 ...
- 在eclipse中安装properties插件PropertiesEditor及设置(附图),ASCII码转换成中文
在eclipse中安装properties插件PropertiesEditor及设置(附图),ASCII码转换成中文安装成功后ASCII还是不能转换成中文的,原因是设置里面没有把编码设置为utf8的, ...
- Dubbo 入门之二 ——- 项目结构解析
本文主要说明点 概述 背景 需求 架构 Dubbo源代码项目结构 概述 分享 Dubbo 的项目结构 ,通过本文可以大致了解到Dubbo整个项目的结构 背景 将一个项目进行拆分, 进行分布式架构. 需 ...
- 简化开发:Lombok的使用
Java中优雅的使用Lombok 1.简介 Lombok 是一种 Java实用工具,可用来帮助开发人员消除Java的冗长,尤其是对于简单的Java对象(POJO), 它通过注释实现这一目的.一个标准的 ...
- 如何扩展VS2017未安装的功能
扩展VS2017未安装的功能 我们在使用VS2017时,由于VS2017该ide功能过于强大,使用范围涵盖多个领域,我们在安装VS2017时很多时候只需要安装自己需要的某部分的功能即可,这个步骤在软件 ...
- C#正则表达式_简单梳理_Emoji表情字符处理
A-最近一直有接触到正则表达式,现对其做简单梳理: private const RegexOptions OPTIONS = RegexOptions.IgnoreCase | RegexOption ...
- [转]Mysql FROM_UNIXTIME as UTC
本文转自:https://stackoverflow.com/questions/18276768/mysql-from-unixtime-as-utc You would be better off ...
- 从零开始学安全(十)●TCP/IP协议栈
局域网靠mac 地址通信
- [PHP]算法-二进制中1的个数的PHP实现
二进制中1的个数: 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 思路: 1.右移位运算>> 和 与运算& 2.先移位个然后再与1 &运算为1的就是1 ...