C++11新特性【右值引用】

文章目录

    • 1. 什么是左值
    • 2. 什么是右值
    • 3. 左值引用
    • 4. 左值引用使用场景
    • 5. 右值引用
    • 6. 右值引用使用场景
      • 6.1 场景1
      • 6.2 场景2
    • 7. 完美转发

1. 什么是左值

左值不能根据字面意思来理解,不是说在左边的就是左值,例如:

int main()
{int a = 0;int b = a;return 0;
}

这里的int b = aa在右边,但a也是左值。

左值可以对它进行取地址赋值,可以出现在赋值=符号的左边

int main()
{//左值int a = 0;int b = a;int* ptr = new int(11);const int ca = 100;return 0;
}

2. 什么是右值

右值出现在赋值符号的=右边,不能出现在赋值符号的左边,不能对其进行取地址,例如:

image-20240312090818335

对于"hello world"这样的常量字符串,它本身是右值,但是:

const char *pch = "hello world";

这个也能对其进行取地址,本质上编译器做出了隐式类型转换,将字符串字面值转换成指向字符常量的指针,将该字符串的首元素地址给了pch

3. 左值引用

引用在语法上来说就是取别名,左值引用就是给左值取别名:

int main()
{int a = 10;int& b = a;	//左值引用return 0;
}

4. 左值引用使用场景

左值引用大多数用于做参数传参做返回值,采用左值引用的核心价值就是减少拷贝

左值引用缺陷:

但是左值引用有一个缺陷就是不能返回局部对象

string& func()
{string s; cin >> s;//...return s;	//退出函数生命周期结束
}

这里就算是返回的引用,但是这个局部对象的生命周期到了,所以只能传值返回,这里就有2次拷贝,生成临时对象拷贝一次,赋值又拷贝一次。

class A
{
public:A() :s(""){cout << "A()" << endl;}A(const string& str) :s(str){cout << "A(const string &str):s(str)" << endl;}A(const A& copy) :s(copy.s){cout << "A(const A& copy)" << endl;}~A(){cout << "~A()" << endl;}
private:string s;
};A func()
{A a;return a;
}int main()
{A ta = func();cout << endl;A tb;tb = func();cout << endl;A tc(tb);
}//	输出:
//	A()
//	A(const A& copy)
//	~A()
//	
//	A()
//	A()
//	A(const A& copy)
//	~A()
//	~A()
//	
//	A(const A& copy)
//	~A()
//	~A()
//	~A()

5. 右值引用

右值引用即给右值取别名,用&&表示:

int Add(int x, int y)
{return x + y;
}int main()
{int&& a = 10;//int& b = Add(2, 3);	//errorint&& b = Add(2, 3);return 0;
}

左值引用也可以引用,但是要加上const

int main()
{int x = 20;int y = 30;const int& a = 10;const int& ret = x + y;return 0;
}

右值引用需要加上move才可以引用左值

int main()
{int x = 100;int&& y = move(x);return 0;
}

6. 右值引用使用场景

右值引用的核心价值也是减少拷贝,弥补左值引用的缺陷(传值返回)

6.1 场景1

自定义类型深拷贝必须返回的场景:

namespace mystring
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s):_str(nullptr){cout << "string(const string& s) -- 深拷贝" << endl;string tmp(s._str);swap(tmp);}string(string&& s):_str(nullptr){cout << "string(string&& s) -- 移动拷贝" << endl;swap(s);}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}string& operator=(string&& s){cout << "string& operator=(string && s) -- 移动拷贝" << endl;swap(s);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity; // 不包含最后做标识的\0};
}mystring::string func()
{mystring::string str("hello");return str;
}int main()
{mystring::string ret1;ret1 = func();cout << endl;mystring::string ret2 = func();return 0;
}

image-20240312114340818

内置类型的右值叫做纯右值自定义类型的右值叫做将亡值

因为内置类型的生命周期只在表达式这一行,再往后走就析构了,例如mystring::string ret2 = func(),这个func()的生命周期就在这一行。

右值引用就是资源互换,将亡值的资源拿过来,然后把自己不用的内容给将亡值带走

编译器会进行优化:

  1. 连续的构造/拷贝构造合并;
  2. str强行识别成右值——将亡值(每个编译器都这样!)

如果说我们要拷贝的对象是一棵树,这样减少了拷贝的资源,极大提升了性能

6.2 场景2

容器的插入接口,插入对象是右值,可以利用移动构造转移资源,减少拷贝

int main()
{list<mystring::string> lt;mystring::string s1("hello list1");lt.push_back(s1);cout << endl;mystring::string s2("hello list2");lt.push_back(move(s2)); lt.push_back("hello list3");	//插入对象是右值
}

所有的容器都实现了右值版本:

image-20240312125531560

7. 完美转发

在模板中&&不代表右值引用,而是万能引用,即既能接收左值又能接收右值

void Func(int& x)
{cout << "左值引用" << endl;
}
void Func(int&& x)
{cout << "右值引用" << endl;
}
void Func(const int& x)
{cout << "const 左值引用" << endl;
}
void Func(const int&& x)
{cout << "const 右值引用" << endl;
}template<class T>
void PerfectForward(T&& t)
{Func(t);
}int main()
{int a = 20;PerfectForward(a);	//左值PerfectForward(100);	//右值const int cb = 30;PerfectForward(cb);	//const左值PerfectForward(move(cb));	//const右值return 0;
}

这里运行发现,结果全部都被识别成了左值:

image-20240312154642863

这里是因为引用的底层,都是指针,例如:

int main()
{int a = 10;int& lr = a;int&& rr = move(a);cout << &lr << endl;cout << &rr << endl;
}

这里的lr是左值,而rr的属性也是左值,右值是不能够修改和取地址的,而这里的rr既能取地址也能修改。

这里有点绕,比如说hello,这个字符串是右值,而string&& s = hellos是它的右值引用,s会开一个空间把这个值存起来,s的属性还是属于左值的,因为它能够修改和取地址

右值引用变量的属性会被编译器识别成左值,不然在移动构造的场景下,就无法进行资源转移。

要想其保持原有属性:左值引用左值属性,右值引用右值属性

可以完美转发forward

void PerfectForward(T&& t)
{Func(forward<T>(t));
}

image-20240312161139807

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

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

相关文章

Windows11安装NodeJS18并配置环境变量

从官网下载&#xff0c;或者从百度网盘下载 解压下载的zip包&#xff1a; 重命名为nodejs&#xff1a; 在nodejs中添加cache和global两个目录&#xff1a; 将nodejs和nodejs\global添加到环境变量&#xff1a; 打开终端&#xff0c;输入&#xff1a; node -v接着配置…

高效备考2024年AMC10:吃透2000-2023年1250道真题(限时免费送)

我们今天继续来随机看5道AMC10真题&#xff0c;以及详细解析&#xff0c;这些题目来自1250道完整的官方历年AMC10真题库。通过系统研究和吃透AMC10的历年真题&#xff0c;参加AMC10的竞赛就能拿到好名次。 即使不参加AMC10竞赛&#xff0c;初中和高中数学一定会学得比较轻松、…

(开源项目)OpenHarmony、社区共建Sample合入要求

1.新增Sample功能不能重复于当前已有Sample的功能&#xff1b; 2.新增Sample的工程推荐使用ArkTS语言编写&#xff1b; 3.新增Sample的工程推荐使用Stage模型编写&#xff1b; 4.新增Sample的工程中需要包含UI自动化用例&#xff08;ohosTest工程模块&#xff09;&#xff0…

浅谈船舶岸电系统绝缘监测及故障定位需求及应用

彭姝麟 Acrelpsl 0 项目背景 随着现代船舶发展&#xff0c;船舶电气化程度越来越高&#xff0c;船舶电站的的容量也越来越大&#xff0c;随之而来的是电网的绝缘问题更加复杂化。船舶电力系统一般采用IT系统&#xff0c;即不接地系统。IT系统的优点是发生单相接地时不会出现TN…

2014

1,写出计算Ack(m,n)的递归算法 #include<iostream> using namespace std; int A(int m,int n){if(m0){return n1;}else if(m>0&&n0){return A(m-1,1);}else{return A(m-1,A(m,n-1));} }int main(){int m,n;cout<<"please input two number"&l…

SpringCloud Hystrix 断路器

一、前言 接下来是开展一系列的 SpringCloud 的学习之旅&#xff0c;从传统的模块之间调用&#xff0c;一步步的升级为 SpringCloud 模块之间的调用&#xff0c;此篇文章为第五篇&#xff0c;即介绍 Hystrix 断路器。 二、概述 2.1 分布式系统面临的问题 复杂分布式体系结构中…

了解融云敏感词过滤规则和匹配效果

使用即时通讯&#xff08;IM&#xff09;服务内置了开箱即用的敏感词机制时&#xff0c;可能需要在在开发者后台 配置内置敏感词服务 2 添加敏感词。本文描述了敏感词字符串的设置规则和匹配效果。 简体、繁体智能过滤 设置中文简体敏感词后&#xff0c;对应繁体敏感词也会自…

Scrapy 爬虫框架

网络爬虫框架scrapy &#xff08;配置型爬虫&#xff09; 什么是爬虫框架&#xff1f; 爬虫框架是实现爬虫功能的一个软件结构和功能组件集合爬虫框架是个半成品&#xff0c;帮助用户实现专业网络爬虫 scrapy框架结构("52"结构) spider: 解析downloader返回的响…

BM1684X搭建sophon c++环境

1:首先安装编译好sophon-sail 比特大陆BM1684X开发环境搭建--SOC mode-CSDN博客 2:在将之前配置的soc-sdk拷贝一份到sdk根目录&#xff0c;将交叉编译好的sail中的build_soc拷贝至soc-sdk文件夹内&#xff1b; cp -rf build_soc/sophon-sail/inlcude soc-sdk cp -rf build_soc…

国创证券|沪指、深指、创指、科创50、北证50都是什么意思?

股票指数能够反映股票商场的全体价格变化状况&#xff0c;包含股票的平均价格或市值的变化。出资者能够经过股票指数来了解多种股票的价格变化&#xff0c;从而检测自己的出资作用&#xff0c;预测股市的动向。下面&#xff0c;国创证券就为咱们介绍下常用的股票指数。 1、上证…

Liunx文件系统和基础IO

文件系统和基础IO 基础IOc语言基础IO函数当前路径和标准流系统IO系统调用函数重定向FILE文件结构体 在谈缓存区问题理解文件系统初识inode 基础IO c语言基础IO函数 打开与关闭 FILE *fopen(char *filename, const char *mode);选项还可以是 r/w/a 意味着为可读可写打开。 2…

2024年共享WiFi项目到底怎么样?

共享WiFi项目是近年来兴起的一种新型商业模式&#xff0c;商家通过在自己店铺升级wifi链接模式使其数字化&#xff0c;让用户能够方便地连接到互联网&#xff0c;提升到店体验&#xff0c;增加线上引流。这一项目的出现&#xff0c;为人们的生活带来了诸多便利&#xff0c;同时…