C++右值引用简介

devtools/2024/9/20 2:07:20/ 标签: c++, java, 开发语言

文章目录

    • 一、前言
    • 二、左值和右值
    • 三、左值引用和右值引用
    • 四、右值引用的主要用途
      • 1、移动语义(Move Semantics)
      • 2、完美转发(Perfect Forwarding)
    • 五、移动构造函数与移动赋值运算符
    • 六、总结

一、前言

C++ 的右值引用(rvalue reference)是在 C++11 中引入的,它主要用于实现移动语义和完美转发,极大地提高了程序的性能和灵活性。

二、左值和右值

左值(Lvalue): 可以在程序中取地址的值,通常表示持久性的对象。例如,变量、数组元素、对象成员等。
右值(Rvalue): 不能取地址的临时值,通常是表达式的结果。例如,3, x + y等。
可以形象记忆为可以取地址,位于等号左边就是左值不可以取地址,位于等号右边就是右值

三、左值引用和右值引用

左值引用: 标志是 ‘&’ ,能指向左值不能指向右值的就是左值引用。(存在特殊的情况就是const左值引用可以指向右值:const int &a = 5。)
右值引用: 标志是 ‘&&’,可以指向右值不能指向左值的就是右值引用。(也存在特殊情况可以使用std::move()把左值变为右值来供右值引用使用:int &&a = std::move(b)。)

四、右值引用的主要用途

1、移动语义(Move Semantics)

移动语义允许对象的资源从一个对象“移动”到另一个对象,而不是复制它们。通过 std::move(),可以将对象显式转换为右值引用,从而调用移动构造函数或移动赋值运算符。

class MyClass {
public:MyClass() { std::cout << "Default Constructor\n"; }MyClass(const MyClass&) { std::cout << "Copy Constructor\n"; }MyClass(MyClass&&) { std::cout << "Move Constructor\n"; }
};int main() {MyClass obj1;MyClass obj2 = std::move(obj1);  // 调用移动构造函数return 0;
}
// 输出如下
// Default Constructor
// Move Constructor

在这个例子中,std::move(obj1) 将 obj1 转换为右值引用,从而调用 MyClass 的移动构造函数,而不是复制构造函数。这可以避免深拷贝的开销,提高性能。

2、完美转发(Perfect Forwarding)

在泛型编程中,有时我们希望将参数原封不动地传递给另一个函数,而不管它是左值还是右值。这时可以使用 std::forward 与右值引用配合,实现完美转发。

template<typename T>
void wrapper(T&& arg) {process(std::forward<T>(arg));
}void process(int& x) { std::cout << "Lvalue reference\n"; }
void process(int&& x) { std::cout << "Rvalue reference\n"; }int main() {int a = 10;wrapper(a);        // 输出: Lvalue referencewrapper(20);       // 输出: Rvalue referencereturn 0;
}

(1)int& x表示左值引用,接受可以取地址的变量。
int&& x表示右值引用,接受不能取地址的临时对象或通过std::move转换后的对象。
(2)在模板上下文中,T&&不是单纯的右值引用,而是一种“万能引用”(Universal Reference)。当传递给wrapper函数的参数是左值时,T推导为int&,此时T&&会折叠为int& &,也就是int&。当参数是右值时,T推导为int,此时T&&就是int&&。
(3)std::forward(arg)根据T的推导情况,决定是否转发为左值或右值。如果T是左值引用类型(如int&),std::forward将保持其为左值;如果T是非引用类型(如int),std::forward将其转发为右值。

  • 完美转发的应用场景
// 通用工厂函数:在工厂函数中,创建对象时保留传入参数的左值/右值属性。
template<typename T, typename... Args>
std::shared_ptr<T> make_shared_object(Args&&... args) {return std::make_shared<T>(std::forward<Args>(args)...);
}// 包装函数:编写包装函数时,转发所有参数给另一个函数,确保原有的左值/右值属性不变。
template<typename Func, typename... Args>
auto invoke(Func&& func, Args&&... args) {return std::forward<Func>(func)(std::forward<Args>(args)...);
}// 高效转移资源:在需要高效转移资源的场景中,完美转发可以避免不必要的拷贝,提升性能。

五、移动构造函数与移动赋值运算符

为了充分利用右值引用和移动语义,C++11 引入了移动构造函数和移动赋值运算符。
移动构造函数: 接受一个右值引用参数,并且通常将资源从传入对象“移动”到新对象中。它的主要目的是避免不必要的深拷贝,减少资源开销。

class MyClass {
private:int* data;public:// 默认构造函数MyClass(int size) : data(new int[size]) {std::cout << "Constructing MyClass\n";}// 移动构造函数MyClass(MyClass&& other) noexcept : data(other.data) {std::cout << "Moving MyClass\n";other.data = nullptr; // 将其他对象的指针置为nullptr,防止双重释放}// 析构函数~MyClass() {std::cout << "Destroying MyClass\n";delete[] data;}// 禁用拷贝构造函数(可选)MyClass(const MyClass&) = delete;// 禁用拷贝赋值运算符(可选)MyClass& operator=(const MyClass&) = delete;
};int main() {MyClass obj1(10);            // 调用默认构造函数MyClass obj2(std::move(obj1)); // 调用移动构造函数return 0;
}
// 输出如下
// Constructing MyClass
// Moving MyClass
// Destroying MyClass

移动构造函数的实现: MyClass(MyClass&& other) noexcept : data(other.data)中,other是一个右值引用,指向要被移动的对象。我们将other.data的资源直接移动给当前对象,并将other.data置为nullptr,以防止other在其析构时再次释放该资源。

std::move 的作用: std::move(obj1)将obj1转化为右值引用,从而使其可以匹配移动构造函数。std::move本质上并不移动对象,而是将其类型转换为右值引用类型。

noexcept 关键字: 在移动构造函数中,noexcept关键字表明此函数不会抛出异常。这是一个很好的实践,因为某些标准库容器(如std::vector)要求其元素的移动操作是无异常的,以进行某些优化。

移动构造函数被调用的情况:

// 返回局部对象
MyClass createMyClass() {MyClass temp(10);return temp; // 移动构造函数可能在这里调用
}// 传递临时对象
MyClass obj = MyClass(10); // 临时对象会通过移动构造函数传递给 obj// 通过std::move显式地将对象转为右值引用
MyClass obj1(10);
MyClass obj2(std::move(obj1)); // 使用 std::move 转为右值引用

移动赋值运算符: 类似于移动构造函数,但它是用于对象已经存在时的赋值。

class MyClass {
private:int* data;public:// 移动赋值运算符MyClass& operator=(MyClass&& other) noexcept {std::cout << "Move assigning MyClass\n";if (this != &other) {delete[] data;            // 释放当前对象的资源data = other.data;        // 接管其他对象的资源other.data = nullptr;     // 将其他对象的指针置为 nullptr}return *this;}// 其他构造函数和析构函数省略...
};int main() {MyClass obj1(10);MyClass obj2(20);obj2 = std::move(obj1); // 调用移动赋值运算符
}
// 输出如下
// Move assigning MyClass
// Destroying MyClass

六、总结

右值引用是 C++11 中引入的强大特性,主要用于实现移动语义和完美转发。通过右值引用,C++ 可以避免不必要的对象复制,提高程序的性能,特别是在处理大量数据或复杂对象时。掌握右值引用和相关的移动语义对现代 C++ 编程至关重要。


http://www.ppmy.cn/devtools/96926.html

相关文章

机器学习辅助复合材料预测,性能管理优化创新材料,这种王炸般的组合,还真是大开眼界!

在人工智能与复合材料技术融合的背景下&#xff0c;复合材料的研究和应用正迅速发展&#xff0c;创新解决方案层出不穷。从复合材料性能的精确预测到复杂材料结构的智能设计&#xff0c;从数据驱动的材料结构优化到多尺度分析&#xff0c;人工智能技术正以其强大的数据处理能力…

css通过keyframes实现文字定时向上滚动

一、效果 二、代码 <!DOCTYPE html> <html lang="en"><head>

2-70 基于matlab的三维装载约束下的货物循环取货路径优化模型建立

基于matlab的三维装载约束下的货物循环取货路径优化模型建立。包含文档说明。以配送中心为原点&#xff0c;分派多辆同一规格的货车到n个供应商处取货&#xff0c;最后回到配送中心。要求充分考虑货物车厢中的三维装载位置&#xff0c;确保每个零部件均能成功装载&#xff0c;尽…

《AI办公类工具PPT系列之二——iSlide AI》

一.简介 官网:iSlide- 让PPT设计简单起来 | PPT模板下载平台 iSlide AI是一款基于人工智能技术的PPT制作工具,它可以帮助用户快速高效地创建演示文稿 二.功能介绍 1. AI一键生成PPT 文档导入与解析:用户可以直接上传本地文档(如Word、Markdown、思维导图等),iSlide A…

opencv 控制鼠标键盘实现功能setMouseCallback

鼠标事件类型 OpenCV 支持多种鼠标事件类型&#xff0c;常见的包括&#xff1a; cv2.EVENT_LBUTTONDOWN&#xff1a;左键按下 cv2.EVENT_RBUTTONDOWN&#xff1a;右键按下 cv2.EVENT_MBUTTONDOWN&#xff1a;中键按下 cv2.EVENT_LBUTTONUP&#xff1a;左键释放 cv2.EVENT_RBUTT…

【杂乱算法】前缀和

前缀和 文章目录 前缀和一维应用 二维 一维 一个数组prefix中&#xff0c;第i个元素表示nums[0]至nums[i-1]的总和&#xff0c;那么我们就称这个prefix数组是nums数组的前缀和。 prefix [ i ] ∑ j 0 i nums [ j ] \text{prefix}[i] \sum_{j0}^{i} \text{nums}[j] prefix[…

Android 架构模式之 MVC

目录 架构设计的目的对 MVC 的理解Android 中 MVC 的问题试吃个小李子ModelViewController 大家好&#xff01; 作为 Android 程序猿&#xff0c;MVC 应该是我们第一个接触的架构吧&#xff0c;从开始接触 Android 那一刻起&#xff0c;我们就开始接触它&#xff0c;可还记得我…

手机群控爬取实战

准备工作 准备多部手机&#xff0c;或者模拟器然后将它们于与电脑连接&#xff0c;然后通过 adb 命令查看连接状态 模拟器设置链接&#xff1a; 写文章-CSDN创作中心 安装一个库&#xff1a; pip install adbutils 测试是否链接 输入 adb devices 如果结果中的第二列显示…

安卓相关环境配置

安卓相关环境配置 偶尔更新。。。 JEB&#xff08;动态调试好用&#xff09; JEB动态调试Smali-真机/模拟器&#xff08;详细&#xff0c;新手必看&#xff09; 夜步城 JADX官网&#xff08;静态分析&#xff09; https://github.com/skylot/jadx/releases/tag/v1.5.0 雷…

最新图像修复论文汇总(2024年以来)(三)

汇总了自2024年以来新提出的高质量图像修复工作&#xff0c;包含扩散模型、transformer、mamba、sam等最前沿的技术&#xff0c;其中一些是ICLR、ICML、CVPR、ECCV、ACM MM 2024年的新作。 这里是第三部分&#xff0c;还有两部分请参阅。 最新图像修复论文汇总&#xff08;20…

redis散列若干记录

字典 redis本身使用字典结构管理数据 redis使用hash表实现字典结构 使用了什么hash算法 使用SipHash算法&#xff0c;该算法能有效防止Hash表碰撞&#xff0c;并有不错的性能 hash冲突怎么解决 使用链表法解决hash冲突 hash表如何扩容 渐进式扩容&#xff0c;不会引起线程长期阻…

springboot的自动配置原理

一、自动配置的原理 Spring Boot 的自动配置原理主要基于以下几个核心概念和步骤&#xff1a; 1. 条件注解 (Conditional Annotations)&#xff1a;Spring Boot 使用 Conditional 系列注解来决定是否加载某个配置类或 Bean。这些注解包括但不限于&#xff1a; ConditionalO…

机器学习中的多模态学习

多模态学习(MultiModal Machine Learning,MMML)是一种结合多种不同类型或模态的数据和信息进行统一建模和分析的学习方法。其核心目标是通过机器学习的方法实现对多源模态信息的处理和理解。 多模态学习的基本概念与定义 多模态学习可以涵盖各种不同的数据类型,如图像、文…

ESP32CAM人工智能教学18

ESP32CAM人工智能教学18 获取数据并显示 如果我们给ESP32Cam外挂一些传感器&#xff08;比如温湿度传感器、超声波测距传感器、红外人体传感器等&#xff09;&#xff0c;我们怎么把ESP32Cam捕获到的数据&#xff0c;传递到客户端的浏览器&#xff0c;并在网页index.html中显示…

Redis非关系型数据库

Redis是什么 Redis&#xff1a;REmote DIctionary Server&#xff08;远程字典服务器&#xff09; 是完全开源免费的&#xff0c;用C语言编写的&#xff0c;遵守BSD协议&#xff0c;是一个高性能的&#xff08;Key/Value&#xff09;分布式内存数据 库&#xff0c;基于内存运行…

react18 + ts 使用video.js 直播.m3u8格式的视频流

一、安装依赖 我使用的video.js版本是8.17.3&#xff0c;从 Video.js 7.x 开始&#xff0c;HLS 支持被内置到了 Video.js 中所以不需要安装其他依赖 npm i video.js 二、创建VideoPlayer组件 import React, { useEffect, useRef } from react import videojs from video.js …

使用Webstorm进行高效的全栈JavaScript开发

使用WebStorm进行高效的全栈JavaScript开发是一个很好的选择&#xff0c;因为WebStorm是JetBrains出品的一款功能强大的IDE&#xff0c;专为前端和后端开发者设计&#xff0c;支持JavaScript、TypeScript、Node.js、React、Vue.js、Angular等多种技术栈。以下是一些建议&#x…

Linux软件编程day(12) -udp

任务&#xff1a; 1.利用套接字实现两台主机全双工通信 socket socket bind 发一次数据(数据可以随便) 接收一次数据(目的&#xff1a;接收发送方的IPPort) 两个任务 …

saas服务,对同一个功能,需要使用不同客户的接口。那么哪种设计模式可以解决我的问题?

Q: 我现在遇到的问题&#xff1a;我在做一个saas服务&#xff0c;现在面对多家客户。对同一个功能&#xff0c;需要使用不同客户的接口。比如&#xff0c;我的发送短信功能&#xff0c;每个客户的发消息接口都不同。那么哪种设计模式可以解决我的问题&#xff0c;可以使用c#来给…

Redis:缓存击穿,缓存穿透,缓存雪崩

缓存穿透 缓存和数据库中都没有的数据&#xff0c;可用户还是源源不断的发起请求&#xff0c;导致每次请求都会到数据库&#xff0c;从而压垮数据库。 这将导致这个不存在的数据每次请求都要到存储层去查询&#xff0c;失去了缓存的意义。 *** 解决方案** 对空值进行缓存标…