IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 左值/右值引用&移动/拷贝&完美转发 -> 正文阅读

[C++知识库]左值/右值引用&移动/拷贝&完美转发

左值引用&右值引用

我一度认为是否会用左值/右值引用能够判断出一个人是否了解modern C++

左值:理解为等号左边的值(能取地址)

右值:理解为等号右边的值,即表达式结束后不再存在的临时对象(不能取地址),比如常量表达式,函数返回值,和存在寄存器中的临时值等

纯右值:纯粹的字面量,比如非引用的返回值、表达式(包括lambda)等

将亡值:可以被移动的临时值(第一次看到可能比较难理解,不妨先往后看)

引用:一个变量的别名(没去了解过底层原理,感觉像是指针的某种高级封装)

对引用的操作就相当于对地址中值的操作,也就是给引用变量赋值的前提是,被引用的变量是可以取址的(比如存在寄存器中的变量)。举个例子

int num = 10;		// num是左值,存在内存中
int num1 = num+1;	// num+1是一个表达式,计算的结果肯定是存在寄存器中的
int* pnum = #	// 虽然这个地址是主存中的地址,但是&num这个值是没有一个变量来存储的,&num也是在寄存器中的,是没有主存地址的

此时,如果想对上面的一些变量进行引用,则至少需要知道它们在主存中的地址,也就是是一个左值

int& a = num;	//√
int& a = 1;		//×
int*& p = pnum;	//√
int*& p = # //×

后两个是非常好的例子,pnum是存储在内存中的变量,其值为num的地址,所以可以被引用;&num是临时变量(右值),不在主存中被分配空间,所以不能被引用

也就是说,pnum和&num的值都是num变量的地址,但是它们存在的方式取决了其能否被引用

如果一定要引用临时变量,就需要先将其拷贝到内存中(就像 i n t ? p = & n u m int* p =\&num int?p=&num),然后再去引用内存中的变量,从而间接引用之前的临时变量

以上提到的引用均为左值引用(T&),也是通常提到“引用”时所指的引用

这里不得不提到常量左值引用(const T&),常量左值引用没有左值引用的限制,可以接受右值初始化,但是限定了只读

但是因为右值是用临时变量存储的,赋给左值的时候是做了深拷贝的,然后再销毁临时变量。深拷贝必然带来资源的消耗,所以就需要一种能够直接引用右值的方法

右值引用则是对&num这种右值的引用,不能用左值初始化,一定要用的话需要std::move将左值参数转换为右值

简单来说,右值引用就是延长右值的声明周期,不再做深拷贝,仅仅去引用寄存器中的临时变量。从这个角度来看,右值引用的速度是比左值引用快很多的

移动和拷贝

拷贝:通过一个对象,生成与其相同的另一个对象

移动:把一个对象移动到另一个位置

传统C++在移动时,必须使用先复制再析构的方式,非常反人类。一种更理想的移动方式,就是将旧对象的“使用权限”交给新对象,这就可以通过一个移动构造函数来实现

A(A&& a): p(a.p){
    a.p = nullptr;
}

简单来说,就是通过右值引用拿到旧对象的使用权限,然后解除旧对象自身的引用

也就是说如果打算把一个对象赋值给另一个对象,但是这个对象之后不再打算使用了,就可以用移动的方式减少资源消耗,举个例子

vector<string> v;
string str;
v.push_back(move(str)); // 这步操作之后str为空

完美转发

引用坍塌规则:对引用进行引用时,函数形参 T & & T\&\& T&&不一定能进行右值引用,当传入的是左值时,会被推导为左值。也就是说只有传入右值引用才是真的右值引用

参数转发存在一个问题,引用类型本身是一个左值,所以当传递一个右值引用参数的时候,会被当做左值处理,举个例子

template<typename T>
void pass(T&& v){
    reference(v);
}
void reference(int& v);	// 传递左值
void reference(int&& v);// 传递右值

pass(1);	
int l = 1;	
pass(l);
// pass直接写int&&是不能接收左值参数的,这个模板的写法我也不太熟悉,但是用decltype发现传入左值时v被解释为左值引用了

无论是 p a s s ( 1 ) pass(1) pass(1)还是 p a s s ( l ) pass(l) pass(l),虽然传入pass的一个是左值,一个是右值,但是由于v本身是一个引用,意味着v是一个左值,所以只会调用左值引用的reference

forward函数可以解决引用坍塌的问题,在传递参数时会保持其原有类型

move只是把左值转化为右值,forward只是做类型转换

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-03 15:50:34  更:2022-03-03 15:56:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:31:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码