C++ 中的移动语义

移动语义是 C++11 引入的一项重要特性,它允许对象的资源(如堆上分配的内存)在不进行深度复制的情况下进行转移。通过移动语义,可以将对象的资源从一个对象转移到另一个对象,从而避免不必要的内存拷贝,提高程序性能和效率。

基本用法:

在上面的示例中,move(vec1)将 vec1 转换为右值引用,这允许vec2 的构造函数通过移动语义接管 vec1 的资源,而不是复制它们。这样,vec1 的资源被转移给 vec2,而 vec1 变为空。

#include <iostream>
#include <vector>
using namespace std;
int main()
{vector<int>vecl={1,2,3,4,5};vector<int>vec2=move(vecl);//将vecl的资源移动到 vec2cout<<"vecl size:"<< vecl.size()<< endl;//输出:vecl size:0cout<<"vec2 size:"<< vec2.size()<< endl;//输出:vec2 size:5return 0;
}

移动语义的作用:

优化性能:

减少不必要的复制:使用 move 可以减少在对象赋值或函数返回时发生的不必要的资源复制,特别是对于大型对象或容器,这可以显著提高性能。

       对于对象赋值或者函数返回操作,编译器会根据参数的类型(左值还是右值)来选择调用拷贝构造函数还是移动构造函数。如果没有move,对于一个左值对象,通常会调用拷贝构造函数,这可能导致资源的复制。

      以vector为例,它包含一个指向动态分配数组的指针、容量信息和当前大小信息等。如果在没有移动语义的情况下将一个vector对象赋值给另一个对象,会复制整个数组,这在数组很大时是非常耗时的。

      当我们使用move将一个vector左值转换为右值引用后,在赋值或者返回操作时,编译器会调用移动构造函数。移动构造函数可以简单地将原对象的内部指针(指向动态分配的数组)、容量和大小等信息 “移动” 到新对象中,而不是复制数组中的所有元素。

优化临时对象的使用:在函数参数传递中使用 move 可以避免临时对象的复制,提高效率。

     临时对象是指在表达式求值过程中临时创建的对象,比如函数返回值(如果不是返回引用)或者通过一些运算产生的临时结果。这些临时对象通常在完整的表达式执行完后就会被销毁。

     当我们在函数参数传递中使用move,它可以将左值转换为右值引用,从而触发移动语义。对于一些拥有移动构造函数的对象,移动语义允许将一个对象的资源(如内部指针、计数器等)直接转移到另一个对象,而不是进行复制。

代码如下:

#include <iostream>
#include <utility>
using namespace std;
class A {
public:int* data;size_t size;A(size_t n) : size(n) {data = new int[n];for (size_t i = 0; i < n; ++i) {data[i] = i;}}// 移动构造函数A(A&& other) noexcept {data = other.data;size = other.size;other.data = nullptr;other.size = 0;}// 拷贝构造函数A(const A& other) : size(other.size) {data = new int[size];for (size_t i = 0; i < size; ++i) {data[i] = other.data[i];}}//析构函数~A() {delete[] data;}
};// 函数接收对象按值传递(无move),会触发拷贝构造函数
void p1(A res) {cout << "拷贝构造" << endl;
}// 函数接收右值引用,使用move传递参数可触发移动构造函数
int p2(A&& res) {cout << "移动拷贝构造" << endl;int sum = 0;for (size_t i = 0; i < res.size; ++i){sum += res.data[i];}return sum;
}int main()
{A a(5);// 不使用move,调用p1会触发拷贝构造函数进行复制p1(a);// 使用move,调用p2会触发移动构造函数进行资源转移int cnt=p2(move(a));cout << "计算结果: " << cnt << std::endl;//计算结果为10return 0;
}

实现高效的数据结构:在实现数据结构如动态数组、链表等时,使用 move 可以在元素插入、删除或移动时减少资源复制,提高数据结构的性能。

下面是动态数组的实现,代码如下:

#include <iostream>
#include <utility>
#include <vector>
using namespace std;// 简单的动态数组类实现
class A {
public:vector<int> d;// 在指定位置插入元素,使用move减少复制void insert(int v, int n) {d.push_back(0);  // 先在末尾添加一个占位元素,以便后面移动元素腾出空间// 从最后一个元素开始,逐个将元素向后移动一位,直到指定位置for (int i = d.size() - 1; i > n; --i) {d[i] = move(d[i - 1]);}// 将新元素插入指定位置d[n] = v;}// 删除指定位置的元素,使用move避免不必要的复制void remove(int n) {// 将指定位置之后的元素逐个向前移动一位,覆盖要删除的元素for (int i = n; i < d.size() - 1; ++i) {d[i] = move(d[i + 1]);}// 移除末尾的元素d.pop_back();}
};int main() {A arr;arr.d = { 1, 2, 3, 4, 5 };// 插入元素示例arr.insert(10, 2);// 输出数组元素,验证插入操作for (int num : arr.d) {cout << num << " ";}cout << endl;// 删除元素arr.remove(3);// 再次输出数组元素,验证删除操作for (int num : arr.d) {cout << num << " ";}cout << endl;return 0;
}

注意事项:

使用 move 后,原对象通常处于未定义的状态,不应再使用该对象,在使用 move 时需要谨慎,确保不会导致资源泄露或无效引用。

右值引用

右值应用的定义:

在 C++ 中,右值引用是一种引用类型,用于绑定到右值。右值是指那些不具有持久存储位置或者是即将被销毁的值。例如,字面常量(如5、3.14等)、临时对象(函数返回的临时值等)都是右值。右值引用的语法形式是类型&&,其中&&表示右值引用。

     例如,int&& rref = 5;,这里rref就是一个右值引用,它绑定到了字面常量5这个右值。

实现方式:

移动构造函数

移动构造函数是实现移动语义的关键。它的参数是一个右值引用,用于接收一个即将被销毁的对象的资源。

在这个移动构造函数B(B&& other)中,other是一个右值引用。当一个临时的B对象(右值)被用来构造另一个B对象时,就会调用这个移动构造函数。在函数内部,将other对象的data指针赋值给新对象的data指针,然后将other对象的data指针设置为nullptr,这样就实现了资源(字符串内容)从一个对象到另一个对象的 “移动”,而不是复制。

class B{
public:char* data;B() : data(nullptr) {}B(const char* str) {if (str) {data = new char[strlen(str) + 1];strcpy(data, str);} else {data = nullptr;}}// 移动构造函数MyString(B&& other) noexcept {data = other.data;other.data = nullptr;}~MyString() {delete[] data;}
};

移动赋值运算符

除了移动构造函数,移动赋值运算符operator=(类型&&)也用于实现移动语义。它用于将一个右值引用的对象赋值给另一个对象。

在下面这个移动赋值运算符中,首先检查是否是自我赋值。然后释放当前对象的资源(delete[] data),接着将other对象的资源(data指针)赋值给当前对象,最后将other对象的data指针设置为nullptr,完成资源的移动赋值。这样,当使用右值引用进行赋值操作时,就可以避免不必要的资源复制,实现移动语义。

B& operator=(B&& other) noexcept {if (this!= &other) {delete[] data;data = other.data;other.data = nullptr;}return *this;
}

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

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

相关文章

基于docker进行任意项目灵活发布

引言 不管是java还是python程序等&#xff0c;使用docker发布的优势有以下几点&#xff1a; 易于维护。直接docker命令进行管理&#xff0c;如docker stop、docker start等&#xff0c;快速方便无需各种进程查询关闭。环境隔离。项目代码任何依赖或设置都可以基本独立&#x…

Android 分区相关介绍

目录 一、MTK平台 1、MTK平台分区表配置 2、MTK平台刷机配置表 3、MTK平台分区表配置不生效 4、Super分区的研究 1&#xff09;Super partition layout 2&#xff09;Block device table 二、高通平台 三、展锐平台 四、相关案例 1、Super分区不够导致编译报错 经验…

数据库类型介绍

1. 关系型数据库&#xff08;Relational Database, RDBMS&#xff09;&#xff1a; • 定义&#xff1a;基于关系模型&#xff08;即表格&#xff09;存储数据&#xff0c;数据之间通过外键等关系相互关联。 • 特点&#xff1a;支持复杂的SQL查询&#xff0c;数据一致性和完整…

当产业经济插上“数字羽翼”,魔珐有言AIGC“3D视频创作大赛”成功举办

随着AI技术的飞速发展&#xff0c;3D数字人技术已成为驱动各行各业转型升级的重要力量。在这一背景下&#xff0c;2024山东3D数字人视频创作大赛应运而生&#xff0c;并在一番激烈的角逐后圆满落幕&#xff0c;为科技与创意的交融写下浓墨重彩的一笔。 11月20日&#xff0c;一…

经济增长初步

1.人均产出 人均产出&#xff0c;通常指的是一个国家、地区或组织在一定时期内&#xff0c;每个劳动人口平均创造的生产总值。它是衡量一个地区或国家经济效率和劳动生产率的重要指标。具体来说&#xff0c;人均产出可以通过以下公式计算&#xff1a; 人均产出总产出/劳动人口…

图像增强夜视仪行业全面而深入的分析

图像增强夜视设备&#xff08;I2ND 或 INVD&#xff09;是一种增强监视、安全和军事应用的微光可见度的技术。 它允许用户在非常弱的光线甚至完全黑暗的条件下看到东西。 一、市场研究 1. 市场规模与增长趋势 据QYResearch调研团队最新报告&#xff0c;预计2029年全球图像增强…

002 MATLAB语言基础

01 变量命名规则 变量名只能由字母、数字和下划线组成&#xff0c;且必须以字母开头&#xff1b; 变量名区分字母的大小写&#xff1b; 变量名不能超过最大长度限制&#xff1b; 关键字不能作为变量名&#xff0c;如for、end和if等&#xff1b; 注意&#xff1a;存变量命名时…

greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用

简略图 greater<>()(a, b) a > b 返回true&#xff0c;反之返回false less<>()(a, b) a < b 返回true&#xff0c;反之返回false 在cmp中使用&#xff08;正着理解&#xff09; 规则返回true时a在前&#xff0c;反之b在前 在priority_queue中使用 &#xff…

冲破AI 浪潮冲击下的 迷茫与焦虑

在这个科技日新月异的时代&#xff0c;人工智能如汹涌浪潮般席卷而来&#xff0c;不断改变我们的生活。你是否对 AI 充满好奇&#xff0c;却不知它将如何改变你的工作与生活&#xff1f;又是否会在 AI 浪潮的冲击下陷入迷茫与焦虑&#xff1f;《AI 时代&#xff1a;弯道超车新思…

嵌入式LVGL自定义纯数字键盘

嵌入式LVGL自定义纯数字键盘 一、前言二、设置自定义数字键盘三、使用一、前言 嵌入式UI项目中有时候会使用到纯数字密码的需求,所以打算使用LVGL构建自定义的纯数字键盘。 二、设置自定义数字键盘 参考这个文章,以LV_KEYBOARD_MODE_USER_1为例,增加一个数字键盘,如下图所…

第6篇 寻找最大数___ARM C语言程序<二>

Q&#xff1a;如何创建基于ARM处理器的C语言程序寻找一组数据列表中的最大数呢&#xff1f; A&#xff1a;和基于Nios II处理器的C语言程序一样&#xff0c;在ARM处理器C语言中也使用printf库函数显示程序的运行结果&#xff0c;若要调用printf函数&#xff0c;必须在C程序中包…

【11.22更新】Win11 24H2正式版:26100.2454镜像一键获取!

今日&#xff0c;系统之家小编就给大家带来2024年11月最新推出的Windows11 24H2正式版系统&#xff0c;该版本系统包含最新可选更新补丁KB5046740&#xff0c;用户安装后版本号升至26100.2454。更新此系统后&#xff0c;用户就能通过文件资源管理器和桌面上的右键菜单将内容分享…

python小课堂(一)

基础语法 1 常量和表达式2 变量和类型2.1 变量是什么2.2 变量语法 3 变量的类型3.1 动态类型特性 4 注释4.1注释是什么 5 输入输出5.1 print的介绍5.2 input 6 运算符6.1 算术运算符在这里插入图片描述6.2 关系运算符6.3 逻辑运算符6.4赋值运算符 1 常量和表达式 在print()中可…

区块链网络示意图;Aura共识和Grandpa共识(BFT共识)

目录 区块链网络示意图 Aura共识和Grandpa共识(BFT共识) Aura共识 Grandpa共识(BFT共识) Aura与Grandpa的结合 区块链网络示意图 CP Blockchain:这是中央处理区块链(或可能指某种特定的处理单元区块链)的缩写。它可能代表了该区块链网络的主要处理或存储单元。在这…

springboot整合hive

springboot整合hive pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.…

跨视角差异-依赖网络用于体积医学图像分割|文献速递-生成式模型与transformer在医学影像中的应用

Title 题目 Cross-view discrepancy-dependency network for volumetric medical imagesegmentation 跨视角差异-依赖网络用于体积医学图像分割 01 文献速递介绍 医学图像分割旨在从原始图像中分离出受试者的解剖结构&#xff08;例如器官和肿瘤&#xff09;&#xff0c;并…

WebApis学习笔记,第二节:高级语法

WebApis学习笔记&#xff0c;第二节&#xff1a;高级语法 一、JS组成 我们再回顾一下JS的组成&#xff1a;ECMAScript: 规定了js基础语法核心知识。 比如&#xff1a;变量、分支语句、循环语句、对象等等Web APIs : DOM 文档对象模型&#xff0c; 定义了一套操作HTML文档的AP…

二叉树路径相关算法题|带权路径长度WPL|最长路径长度|直径长度|到叶节点路径|深度|到某节点的路径非递归(C)

带权路径长度WPL 二叉树的带权路径长度(WPL)是二叉树所有叶节点的带权路径长度之和&#xff0c;给定一棵二叉树T&#xff0c;采用二叉链表存储&#xff0c;节点结构为 其中叶节点的weight域保存该节点的非负权值&#xff0c;设root为指向T的根节点的指针&#xff0c;设计求W…

飞桨大模型PaddleOCR

一、新建项目PaddleOCRProject 二、查看开源 pip install paddlepaddle pip install paddleocr指定镜像源下载才快&#xff1a; pip install paddlepaddle -i https://pypi.tuna.tsinghua.edu.cn/simple pip install paddleocr -i https://pypi.tuna.tsinghua.edu.cn/simple 三…

Python创建虚拟环境报错:Error: Command......

文章目录 环境说明问题描述原因分析解决方法 环境说明 系统 # lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.4 LTS Release: 22.04 Codename: jammyPython版本 # python3 --version Python 3.13.0问题描…