当前位置: 首页 > news >正文

C++对象移动

在某些情况下,对象拷贝后就立即被销毁了,这时利用新标准(C++11)提供的对象移动而非拷贝将大幅提升性能.

1.右值引用

为了支持移动操作,c++11新增了一种引用 - 右值引用(rvalue reference)。这种引用必须指向右值,使用&&声明。

右值引用只能引用临时变量或常量值.

右值引用主要是为类的移动语句做准备的。


int main()
{//int& a = 100; //错误int&& ra = 100;//右值引用,引用一个常量int b = 15;//int& b1 = b * 2 + 10;//错误,不能引用临时变量int&& rb = b * 2 + 10;//合法//double &c = sqrt(100);//错误,不能引用临时变量double&& rc = sqrt(100);//合法cout << ra << "," << rb << "," << rc << endl;return 0;
}

2.移动构造函数和移动赋值函数

在C++中,移动构造函数和移动赋值函数是与对象的资源管理相关的两种操作,它们通常用于优化性能,特别是在处理像动态分配数组或复杂数据结构等资源密集型对象时。这两种操作依赖于C++11引入的右值引用和移动语义。

1)移动构造函数

移动构造函数是一个特殊的构造函数,它接受一个右值引用作为参数。当对象被临时创建并立即用作初始化另一个对象时,会调用移动构造函数。与普通构造函数不同,移动构造函数不会创建新对象的副本(不另外创建需要的资源),它会直接使用原始对象的资源(如动态分配的内存),并将其转移到新的对象。

请注意,原始对象在移动构造后通常不再可用。

示例:


class MyClass {  
public:  MyClass(MyClass&& other) noexcept : data(other.data) {  other.data = nullptr; // 将原始对象的资源置为nullptr,确保它不再拥有这些资源  }  private:  int* data; // 假设MyClass管理动态分配的内存  
};
2)移动赋值函数

移动赋值函数是一个特殊的赋值函数,它接受一个右值引用作为参数。实参是一个临时对象时,会调用移动赋值。与拷贝赋值不同,移动赋值不会创建新对象的副本(不另外创建需要的资源),而是直接使用源对象的资源,并将其转移到目标对象。

请注意,源对象在移动赋值后通常不再可用。

示例:

class MyClass {  
public:  MyClass& operator=(MyClass&& other) noexcept {  if (this != &other) {  delete[] data;     // 释放当前对象的资源  data = other.data; // 窃取其他对象的资源  other.data = nullptr; // 将原始对象的资源置为null  }  return *this;  }  
private:  int* data; // 假设MyClass管理动态分配的内存  
};

注意:

●使用noexcept关键字标记移动构造函数和移动赋值函数是一个好习惯,因为移动操作通常不抛出异常。这有助于编译器进行优化(不需要为可能的异常做额外的准备),例如在某些情况下使用移动而不是拷贝。
●在实现移动构造函数和移动赋值操作符时,必须确保源对象在移动后处于有效但未定义的状态。通常将源对象的资源设置为nullptr或执行其他适当的清理操作。
●如果类定义了移动构造函数或移动赋值操作符,通常也应该定义相应的拷贝构造函数和拷贝赋值操作符,以确保对象可以以期望的方式进行复制。

3.move函数

move:将左值转为右值引用,一般是为了利用(触发)移动语义来提高性能,避免不必要的拷贝.需要引入utility文件

注意:使用move后源对象不能继续使用


#include <iostream>
#include <utility>
#include <string>
using namespace std;
int main()
{string s1 = "quzijie";string s2 = s1;cout << "s1=" << s1 << endl;cout << "s2=" << s2 << endl;string s3 = move(s1);cout << "s1=" << s1 << endl;cout << "s3=" << s3 << endl;return 0;
}

4.一个具体的示例

#include <iostream>  }// 移动构造函数  MyString(MyString&& other) noexcept : len(other.len), data(other.data) {// 将源对象的资源转移到新创建的对象  cout << "MyString 移动 构造函数" << endl;other.data = nullptr;other.len = 0;}//拷贝构造函数MyString(const MyString& other) {cout << "MyString 拷贝构造函数" << endl;len = other.len;data = new char[len + 1];strcpy(data, other.data);}// 赋值运算符 MyString& operator=(const MyString& other) {cout << "MyString = " << endl;if (this != &other) {delete[] data;len = other.len;data = new char[len + 1];strcpy(data, other.data);}return *this;}// 移动赋值运算符 MyString& operator=(MyString&& other) noexcept {cout << "MyString 移动 = " << endl;if (this != &other) {delete[] data;data = other.data;len = other.len;other.data = nullptr;other.len = 0;}return *this;}// 析构函数  ~MyString() {delete[] data;}private:int len;//字符长度char* data;//存放数据的指针
};// 辅助函数,用于创建临时MyString对象  
MyString CreateTempString() {return MyString("quzijie");
}int main() {// 使用构造函数/移动构造函数创建s1  cout << "s1: ";MyString s1 = CreateTempString();// 使用移动构造函数创建s2cout << "s2: ";MyString s2 = move(s1);// 使用移动赋值函数将临时对象赋值给s3cout << "s3: ";MyString s3;s3 = CreateTempString();//使用移动赋值函数将s1赋值给s4cout << "s4: ";MyString s4;s4 = move(s1);return 0;
}

如果使用move函数则一定调用对应的移动语义,如果不使用move函数,那么是否使用移动语义这个由编译器自行决定.

http://www.lryc.cn/news/361212.html

相关文章:

  • “华为杯”第十三届中国研究生 数学建模竞赛-E题:粮食最低收购价政策问题研究(续)
  • (一)django目录介绍
  • leetcode5 最长回文子串
  • 《论文阅读》通过顺序不敏感的表示正则化实现稳健的个性化对话生成 ACL 2023
  • python采集汽车价格数据
  • 德克萨斯大学奥斯汀分校自然语言处理硕士课程汉化版(第四周) - 语言建模
  • Jitsi meet 退出房间后,用户还在房间内
  • Java 18 新特性
  • c++基础创建对象
  • WHAT - 容器化系列(二)- docker
  • 力扣 19题 删除链表的倒数第 N 个结点 记录
  • 渗透测试之Web安全系列教程(二)
  • 【算法】在?复习一下快速排序?
  • matlab安装及破解
  • Tree——输出项目的文件结构(Linux)
  • UE5 读取本地图片并转换为base64字符串
  • 【NOIP普及组】税收与补贴问题
  • Docker 部署 mysql 服务
  • 01- Redis 中的 String 数据类型和应用场景
  • Android音频焦点
  • Docker安全配置
  • 文件上传之使用一个属性接收多个文件
  • chat4-Server端保存聊天消息到mysql
  • vivo鄢楠:基于OceanBase 的降本增效实践
  • arm cortex-m架构 SVC指令详解以及其在freertos的应用
  • k8s笔记——kubernetes中的三种IP
  • Golang | Leetcode Golang题解之第127题单词接龙
  • 微服务中feign远程调用相关的各种超时问题
  • springboot整合chatgpt,并且让其可以记录上下文
  • CTP前端:解码数字世界的魔法师