C++ 右值引用

能出现在赋值号左边的表达式称为“左值”,不能出现在赋值号左边的表达式称为“右值”。一般来说,左值是可以取地址的,右值则不可以。

非 const 的变量都是左值。函数调用的返回值若不是引用,则该函数调用就是右值。之前我们了解的“引用”是引用变量的,而变量是左值,因此它们都是“左值引用”。

C++11 新增了一种引用,可以引用右值,因而称为“右值引用”。无名的临时变量不能出现在赋值号左边,因而是右值。右值引用就可以引用无名的临时变量。定义右值引用的格式如下:

类型 && 引用名 = 右值表达式;

例如:

class A{};
A & rl = A();  //错误,无名临时变量 A() 是右值,因此不能初始化左值引用 r1
A && r2 = A();  //正确,因 r2 是右值引用

引入右值引用的主要目的是提高程序运行的效率。有些对象在复制时需要进行深复制,深复制往往非常耗时。合理使用右值引用可以避免没有必要的深复制操作。例如下面的程序:

#include <iostream>
#include <string>
#include <cstring>
using namespace std;
class String
{
public:char* str;String() : str(new char[1]) { str[0] = 0; }String(const char* s) {str = new char[strlen(s) + 1];strcpy(str, s);}String(const String & s) {//复制构造函数cout << "copy constructor called" << endl;str = new char[strlen(s.str) + 1];strcpy(str, s.str);}String & operator = (const String & s) {//复制赋值号cout << "copy operator = called" << endl;if (str != s.str) {delete[] str;str = new char[strlen(s.str) + 1];strcpy(str, s.str);}return *this;}String(String && s) : str(s.str) { //移动构造函数cout << "move constructor called" << endl;s.str = new char[1];s.str[0] = 0;}String & operator = (String && s) { //移动赋值号cout << "move operator = called" << endl;if (str != s.str) {str = s.str;s.str = new char[1];s.str[0] = 0;}return *this;}~String() { delete[] str; }
};
template <class T>
void MoveSwap(T & a, T & b) {T tmp(move(a));  //std::move(a) 为右值,这里会调用移动构造函数a = move(b);  //move(b) 为右值,因此这里会调用移动赋值号b = move(tmp);  //move(tmp) 为右值,因此这里会调用移动赋值号
}
int main()
{String s;s = String("this");  //调用移动赋值号cout << "* * * *" << endl;cout << s.str << endl;String s1 = "hello", s2 = "world";MoveSwap(s1, s2);  //调用一次移动构造函数和两次移动赋值号cout << s2.str << endl;return 0;
}

程序的输出结果如下:

move operator = called
****
this
move constructor called
move operator = called
move operator = called
hello

第 33 行重载了一个移动赋值号。它和第 19 行的复制赋值号的区别在于,其参数是右值引用。在移动赋值号函数中没有执行深复制操作,而是直接将对象的 str 指向了参数 s 的成员变量 str 指向的地方,然后修改 s.str 让它指向别处,以免 s.str 原来指向的空间被释放两次。

该移动赋值号函数修改了参数,这会不会带来麻烦呢?答案是不会。因为移动赋值号函数的形参是一个右值引用,则调用该函数时,实参一定是右值。右值一般是无名临时变量,而无名临时变量在使用它的语句结束后就不再有用,因此其值即使被修改也没有关系

第 53 行,如果没有定义移动赋值号,则会导致复制赋值号被调用,引发深复制操作。临时无名变量String(“this”)是右值,因此在定义了移动赋值号的情况下,会导致移动赋值号被调用。移动赋值号使得 s 的内容和 String(“this”) 一致,然而却不用执行深复制操作,因而效率比复制赋值号高。

虽然移动赋值号修改了临时变量 String(“this”),但该变量在后面已无用处,因此这样的修改不会导致错误。

第 46 行使用了 C++ 11 中的标准模板 move。move 能接受一个左值作为参数,返回该左值的右值引用。因此本行会用定义于第 28 行、以右值引用作为参数的移动构造函数来初始化 tmp。该移动构造函数没有执行深复制,将 tmp 的内容变成和 a 相同,然后修改 a。由于调用 MoveSwap 本来就会修改 a,所以 a 的值在此处被修改不会产生问题。

第 47 行和第 48 行调用了移动赋值号,在没有进行深复制的情况下完成了 a 和 b 内容的互换。对比 Swap 函数的以下写法:

template <class T>
void Swap(T & a, T & b) {T tmp(a);  //调用复制构造函数a=b;  //调用复制赋值号b=tmp;  //调用复制赋值号
}

Swap 函数执行期间会调用一次复制构造函数,两次复制赋值号,即一共会进行三次深复制操作。而利用右值引用,使用 MoveSwap,则可以在无须进行深复制的情况下达到相同的目的,从而提高了程序的运行效率。

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

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

相关文章

Sublime Text 3 详细安装教程

Sublime Text 3 详细安装 下载 官网下载地址https://www.sublimetext.com/3 安装 1 . 下载完成之后安装程序自动运行&#xff0c;默认安装路径&#xff0c;不要修改&#xff08;如果修改安装路径后期需要配置环境变量&#xff09; 注意&#xff1a;最好使用默认填入的安装…

matlab rgb2hsv v=1,为什么我在matlab中使用rgb2hsv时一直显示这个函数不存在呢?那源函数在哪下载?...

匿名用户1级2014-04-15 回答specfun工具箱没装可能给你个rgb2hsv源代码 复制下来同名保存就能用了function [h,s,v] rgb2hsv(r,g,b)switch nargincase 1,if isa(r, uint8),r double(r) / 255;elseif isa(r, uint16)r double(r) / 65535;endcase 3,if isa(r, uint8),r doubl…

php游戏怎么设置fms,[FMS]FMS使用需要注意的几种问题总结说明

[FMS]FMS使用需要注意的几种问题总结说明&#xff1a;* 中文编码&#xff1a;有些时候我们用flash去读取外部的php&#xff0c;asp.....文件里的中文显示在flash里会出现乱码的情况&#xff0c;为了解决在flash里显示中文很多教程里通常都直接加了一句System.useCodepagetrue问…

STL算法

STL 提供能在各种容器中通用的算法&#xff0c;如插入、删除、查找、排序等。算法就是函数模板。算法通过迭代器来操纵容器中的元素。 许多算法操作的是容器上的一个区间&#xff08;也可以是整个容器&#xff09;&#xff0c;因此需要两个参数&#xff0c;一个是区间起点元素…

oracle导出pck文件,Oracle sqlloader自动化导入迁移工具—可批量生成千万控制文件与批处理文件|一键执行(推荐)...

应用介绍一、设计概述与背景&#xff1a;在目前SQL Server数据库和Oracle数据库之间进行数据迁移工作&#xff0c;可以使用多种方案&#xff0c;其中采用文本文件作为数据交换的中间介质&#xff0c;具有如下特点&#xff1a;1、各个主流数据库都支持文本文件的导入、导出功能。…

STL中“大”、“小”和“相等”的概念

STL 中关联容器内部的元素是排序的。STL 中的许多算法也涉及排序、查找。这些容器和算法都需要对元素进行比较&#xff0c;有的比较是否相等&#xff0c;有的比较元素大小。 在 STL 中&#xff0c;默认情况下&#xff0c;比较大小是通过<运算符进行的&#xff0c;和>运算…

oracle 分割字符成数组,oracle依据分隔符将字符串分割成数组函数

oracle根据分隔符将字符串分割成数组函数--创建表类型create or replace type mytype as table of number;--如果定义成varchar--CREATE OR REPLACE type mytype as table of varchar2(4000);-- 将字符串分割成数组function my_split(piv_str in varchar2, piv_delimiter in va…

C++ vector,STL vector

vector 是顺序容器的一种。vector 是可变长的动态数组&#xff0c;支持随机访问迭代器&#xff0c;所有 STL 算法都能对 vector 进行操作。要使用 vector&#xff0c;需要包含头文件 vector。 在 vector 容器中&#xff0c;根据下标随机访问某个元素的时间是常数&#xff0c;在…

sql查询oracle数据,sql-server – 从SQL Server查询Oracle数据库

我有一个Oracle 11g XE数据库,我想将其转移到SQL Server Express 2005中.起初我以为我只是在Oracle中生成表作为SQL,操纵数据格式,并在SQL Server中运行查询.这适用于小型表,但我有几个表有几十万行,有些表有数百万行,所以这个解决方案不起作用.然后我创建了一个包含以下内容的…

C++ list,STL list

list 是顺序容器的一种。list 是一个双向链表。使用 list 需要包含头文件 list。双向链表的每个元素中都有一个指针指向后一个元素&#xff0c;也有一个指针指向前一个元素。 在 list 容器中&#xff0c;在已经定位到要增删元素的位置的情况下&#xff0c;增删元素能在常数时间…

Oracle010316,安装oracle后登录时出现 ERROR: ORA-01031 insufficient privileges

运行环境&#xff1a;在自己笔记本电脑上安装测试操作系统版本&#xff1a;64位win8.1oracle版本&#xff1a;64位 oracle 11g安装oracle 成功后//以管理员身份登录oracle在cmd里输入命令 sqlplus / as sysdba然后 报错 ERROR: ORA-01031 insufficient privileges解决办法&…

C++ 双向队列

deque 也是顺序容器的一种&#xff0c;同时也是一个可变长数组。要使用 deque&#xff0c;需要包含头文件 deque。所有适用于 vector 的操作都适用于 deque。 deque 和 vector 有很多类似的地方。在 deque 中&#xff0c;随机存取任何元素都能在常数时间内完成。它相比于 vect…

C++ 函数对象

如果一个类将()运算符重载为成员函数&#xff0c;这个类就称为函数对象类&#xff0c;这个类的对象就是函数对象。函数对象是一个对象&#xff0c;但是使用的形式看起来像函数调用&#xff0c;实际上也执行了函数调用&#xff0c;因而得名。 函数对象的例子。 #include <i…

sencha app watch php,我的第一个基于SenchaTouch的WebApp

经过进一周的各种折腾&#xff0c;各种想放弃&#xff0c;各种纠结&#xff0c;最终还是顺利的完成了SenchaTouch的开发&#xff0c;回想起来感觉“甜甜的”&#xff0c;也充分体会到Sencha MVC开发模式的好处&#xff0c;以及SenchaTouch.js和Extjs的强大和牛逼&#xff0c;不…

C++关联容器,STL关联容器

关联容器内部的元素都是排好序的&#xff0c;有以下四种。 set&#xff1a;排好序的集合&#xff0c;不允许有相同元素。multiset&#xff1a;排好序的集合&#xff0c;允许有相同元素。map&#xff1a;每个元素都分为关键字和值两部分&#xff0c;容器中的元素是按关键字排序的…

linux练习 串口跟进程6,linux下串口测试程序

linux下串口测试程序通过简单的参数配置&#xff0c;执行文件串口号波特率#include stdio.h#include stdlib.h#include unistd.h#include sys/types.h#include sys/stat.h#include fcntl.h#include errno.h#include sys/time.h#include time.h#include string.h#include sys/io…

C++ pair类模板

在学习关联容器之前&#xff0c;首先要了解 STL 中的 pair 类模板&#xff0c;因为关联容器的一些成员函数的返回值是 pair 对象&#xff0c;而且 map 和 multimap 容器中的元素都是 pair 对象。pair 的定义如下&#xff1a; template <class_Tl, class_T2> struct pair…

kali linux关闭进程,技术|如何使用 Kali Linux 黑掉 Windows

Kali Linux 派生自 Debian Linux&#xff0c;主要用于渗透测试&#xff0c;拥有超过 300 个的预安装好的渗透测试工具。Metasploit 项目中 Metasploit 框架支持 Kali Linux 平台&#xff0c;Metasploit 是一个用于开发和执行安全利用代码(security exploit)的工具。让我们来使用…

C++ multiset

multiset 是关联容器的一种&#xff0c;是排序好的集合&#xff08;元素已经进行了排序&#xff09;&#xff0c;并且允许有相同的元素。 不能直接修改 multiset 容器中元素的值。因为元素被修改后&#xff0c;容器并不会自动重新调整顺序&#xff0c;于是容器的有序性就会被破…

linux命令中的cp,Linux高级技术:关于cp命令中拷贝所有的写法

Linux高级技术&#xff1a;关于cp命令中拷贝所有的写法发布时间:2007-09-30 21:39:36来源:红联作者:GiftPot今天在编写一个脚本的时候&#xff0c;发现一个比较奇怪的问题&#xff1a;就是在使用cp拷贝当前目录下所有文件到目标目录的时候&#xff0c;源和目标目录大小不同。原…