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));std::cout << "After move, str is \"" << str << "\"\n";std::cout << "The contents of the vector are \"" << v[0]<< "\", \"" << v[1] << "\"\n";
}
复制代码

Output:

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 movetemplate<class _Ty> inlinetypename tr1::_Remove_reference<_Ty>::_Type&&move(_Ty&& _Arg){    // forward _Arg as movablereturn ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg);}
复制代码

 

好吧,看过了这段,可能有人又迷惑了,不是说有名左指变量不能绑定到右值引用上面么?为什么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 _Rightif (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 + 1);else{    // copy pointerthis->_Bx._Ptr = _Right._Bx._Ptr;_Right._Bx._Ptr = 0;}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(0), y(0){}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[100];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 = 9;p.y = 7;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[0].toString()<< "\", \"" << v[1].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++标准已经更新,要求在定义copy构造函数的同时定义move构造函数,虽然现在这种编程方法没有流行,但是我相信以后这将成为另外一个媲美引用的优化运行速度的编程方法,我们拭目以待!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/433433.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python入侵数据库数据库_一个简单的Python访问Mysql数据库例子

2020/11/3操作记录搭建好Python的数据环境之后&#xff0c;接下来就是在Python代码中访问数据库我先在Navicat图形化界面创建一个数据库命名为pythontest&#xff0c;再在数据库中创建了一个表studentinfo有nid,nname,ngrade,nage四个字段&#xff0c;nid为主键递增。通过查询编…

r语言散点图_R语言 | 散点图入门:以声学元音图为例

R语言语言学与R语言的碰撞Xu & YangPhoneticSan学习参考Discovering Statistics Using RStatistics for Linguistics with RHow to Do Linguistics with RR in ActionAnalyzing Linguistic DataR Graphics Cookbook Recap可以使用plot( )函数进行基础的制图。调用ggplot2包…

mysql创建表里主码和外码_SQL语言创建表时候怎么定义主码和外码

展开全部SQL语言创建表时候用Primary Key(属性名)定义主码&#xff0c;用Foreign Key(属性名)定义外码。主码是一种唯一关键字&#xff0c;表定62616964757a686964616fe59b9ee7ad9431333433623064义的一部分。一个表的主码可以由多个关键字共同组成&#xff0c;并且主码的列不能…

unity镜头边缘羽化_【后期修图】如何利用Ps中的自适应广角滤波器校正镜头失真?...

当用广角镜头拍摄对象时&#xff0c;透视问题通常以线条的形式出现&#xff0c;这应该是直的&#xff0c;但看起来是弯曲的和扭曲的。这通常被称为桶形失真&#xff0c;其主要发生在广角镜头中&#xff0c;因为图像的放大率越远&#xff0c;主体离镜头的光轴越远。简单来说&…

用引用返回值(转)

函数返回值时&#xff0c;要生成一个值的副本。而用引用返回值时&#xff0c;不生成值的副本。例如&#xff0c;下面的程序是有关引用返回的4种形式&#xff1a;//*********************//**   ch9_6.cpp  **//********************* #include <iostream.h> float tem…

mysql用binlog恢复数据_利用mysql的binlog恢复数据

MySQL Binary Log也就是常说的bin-log, ,是mysql执行改动产生的二进制日志文件,其主要作用有两个:* 数据回复* 主从数据库。用于slave端执行增删改&#xff0c;保持与master同步。1.开启binary log功能需要修改mysql的配置文件&#xff0c;本篇的实验环境是win7&#xff0c;配置…

python 怎么处理json_Python是怎样处理json模块的

首先&#xff0c;了解下什么是JSON&#xff1f;JSON&#xff1a;JavaScript Object Notation 【JavaScript 对象表示法】JSON 是一种轻量级的数据交换格式&#xff0c;完全独立于任何程序语言的文本格式。一般&#xff0c;后台应用程序将响应数据封装成JSON格式返回。JSON的基本…

UEditor1.2.6.0在.net环境下使用

UEditor1.2.6.0 1.百度百科词条 2.UEditor官方网站 【CKEditorCKFinder的配置实用&#xff0c;可查看博主另一篇文章】 第一次接触UEditor还是在2011年的下半年里&#xff0c;当时由于需要找一款富文本编辑器进行新闻的网站开发&#xff0c;当时UEditor异常较多&#xff0c;就将…

sql 删除数据_从零开始学SQL:是什么、如何安装、基本语法、表格(创建、删除、更新)、数据(插入、删除、更新)...

一、学习知识的黄金圈思维用黄金圈思维分析自己学习SQL&#xff0c;能增加自己的效率和坚持下去的动力。二、SQL基础知识1.数据库概念&#xff1a; database &#xff0c;按照数据结构来组织、存储和管理数据的仓库。2.关系数据库&#xff1a;多张表表之间的关系表&#xff1a;…

函数使用了堆栈的字节超过_在C语言中如何访问堆栈?

堆栈一般是用来保存变量之类的东西&#xff08;静态变量在内存中&#xff0c;虽然堆栈就是内存的一部分&#xff0c;但为了防止歧义&#xff0c;还是分成两部分来说&#xff09;&#xff0c;一般情况下没必要去故意读取堆栈的值&#xff0c;变量用变量名就可以直接访问&#xf…

怎么解决相位抵消_【录音教程】如何防止话筒拾音的声学相位抵消

设想一下你以单声道的模式为一个吉他歌手做扩声&#xff0c;一支人 声听起来比较古怪&#xff0c;有点“空”&#xff0c;同时有滤波器之类的效果声。为什么会这样&#xff1f;主唱话筒拾取歌手的演唱&#xff0c;同时拾取箱琴的话筒也串入了歌手的声音(见图1)。吉 他话筒离主唱…

string 找出所有数字 index_发现规律,解决整数转罗马数字

嗨&#xff0c;各位&#xff01;我们又准时见面了&#xff0c;即将迎来难得的周末时光&#xff0c;我们今天来一道相对简单的题目逻辑梳理的题目&#xff0c;原定的动态规划的常见题型我们放在周末进行更新。话不多说&#xff0c;我们先看题目&#xff1a; 01 . 题目罗马数字包…

ASP.NET MVC的路由

好久没写博文了&#xff0c;感觉最近好像少了点动力。唉&#xff01;这回就看看这个MVC的路由。 说这个路由机制其实不是MVC里面特有的&#xff0c;ASP.NET里面本身就有的&#xff0c;只不过在WebForm里面一般比较少用&#xff0c;而在MVC里就是把原本的路由扩展了。原本对不知…

java情书_Java情书已写好,就差妹子了!

我是Java程序员我用我的方式表达对你的爱&#xff01;我能抽象出整个世界...但是我却不能抽象出你...你肯定是一个单例&#xff0c;因为你是那样的独一无二...所以我的世界并不完整...我可以重载甚至覆盖这个世界里的任何一种方法...但是却不能覆盖对你的思念...也许命中注定了…

windows下手动配置ipv6地址

在XP,2003等早期版本中&#xff0c;ipv6地址在“网络连接”的属性配置里是无法手工配置的&#xff0c;只能使用netsh配置。配置方法如下&#xff1a;首先&#xff0c;安装IPV6协议&#xff0c;ipv6 install 第二步&#xff0c;查看当前使用的本地连接信息&#xff1a; 如上图&a…

java 幽灵引用_Java 幽灵引用的作用

原标题&#xff1a;Java 幽灵引用的作用来源&#xff1a;imzoer&#xff0c;blog.csdn.net/imzoer/article/details/8044900找实习的时候&#xff0c;面试大摩&#xff0c;就遇到了这个问题&#xff0c;当时真不该跟面试官交流这个内容的。垃圾收集过程中&#xff0c;对象的可触…

java assembly 打包_maven 使用assembly 进行打包的方法

1. pom 中添加assembly 插件要使用assembly 进项编译打包&#xff0c; 首先主要在pom 中的build中添加插件信息&#xff0c; 具体如图下所示&#xff1a;${project.artifactId}src/main/javasrc/main/resourcestrue**/*.xml**/*.properties${profile.dir}trueorg.apache.maven.…

rt5350 中断初始化

linux 下的中断初始化&#xff0c;实际就是对 irq_desc 这个结构体进行初始化&#xff0c;其中最关键莫过于 irq_flow_handler_t handle_irq 中断处理函数 struct irqaction *action 用户自己设置的处理函数链表&#xff0c;由hand…

VS2010安装异常中断后无法安装的解决方法(安装时发生严重错误)

最近&#xff0c;因为公司开发的需要&#xff0c;对开发环境进行全面的升级&#xff0c;在这其中也遇到了不少问题&#xff0c;在之后将陆续整理出来&#xff0c;以便以后查看。 之前开发环境&#xff1a;ArcGIS9.3&#xff0c;ArcEngine9.3&#xff0c;Oracle10g&#xff0c;A…

将5350 i2c clk设置为gpio 中断模式的方法

5350和我之前用的三星和全志的芯片在中断这块有点差别&#xff0c;三星和全志的都是有专门的外部中断管脚&#xff0c;并且每个中断管脚对应一个中断号&#xff0c;对管脚寄存器的配置即irq_desc里chip变量&#xff0c;都是bsp里自带的&#xff0c;我们只需要用request_irq来注…