首页 > 编程语言 >C++中的移动语义

C++中的移动语义

时间:2024-11-21 20:14:40浏览次数:3  
标签:String 右值 对象 rhs 语义 C++ 移动 data

一、移动语义
1.定义:

在C++ 中,移动语义是一种优化技术。

移动语义允许资源的“移动”而不是“拷贝”。在传统的 C++ 中,当一个对象被赋值或传递给函数时,通常会发生拷贝操作,这会导致性能下降,尤其是在处理大型对象时。移动语义通过引入右值引用和移动构造函数、移动赋值运算符,允许程序员将资源的所有权从一个对象转移到另一个对象,而不是进行深拷贝。

移动语义的作用:

2. 作用

  • 提高性能:移动语义主要用于避免对象中资源的不必要复制,特别是对于那些包含动态分配内存、文件句柄等资源的对象。它通过转移资源所有权来高效地处理对象的构造、赋值等操作,从而提升程序的运行效率。
  • 优化资源管理:在管理资源的类中,移动语义可以方便地将资源从一个对象转移到另一个对象,使得资源能够更灵活地被重新分配和利用。

下面是一个简单的 String 类,展示移动语义的实现:

#include <iostream>
#include <cstring>
class String {
private:
char* data;
size_t len;
public:
// 普通构造函数
String(const char* cstr = "") {
len = std::strlen(cstr);
data = new char[len + 1];
std::strcpy(data, cstr);
}
// 移动构造函数
String(String&& rhs) noexcept : data(rhs.data), len(rhs.len) {
rhs.data = nullptr;
rhs.len = 0;
}
// 移动赋值运算符
String& operator=(String&& rhs) noexcept {
if (this!= &rhs) {
delete[] data;
data = rhs.data;
len = rhs.len;
rhs.data = nullptr;
rhs.len = 0;
}
return this;
}
~String() {
delete[] data;
}
void print() const {
std::cout << data << std::endl;
}
};

在这个 String 类中:

1)移动构造函数 String(String&& rhs) 接收一个右值引用 rhs 。它将 rhs 对象中的 data 指针和 len 成员的值转移到新构造的对象中,然后将 rhs 对象的 data 指针设为 nullptr , len 设为 0 ,表示资源所有权已经转移。

2)移动赋值运算符 String& operator=(String&& rhs) 的作用类似。它先释放当前对象的资源( delete[] data ),然后将 rhs 对象的资源转移到当前对象,最后将 rhs 对象的资源相关成员重置。

String getString() {
String temp("Hello");
return temp;
}
int main() {
String str = getString();
str.print();
return 0;
}

在 main 函数中, getString 函数返回一个临时 String 对象。在没有移动语义的情况下,这个临时对象的资源( data 中的字符数组)会被复制到 str 对象中。但有了移动语义, str 对象通过移动构造函数直接接管了临时对象的资源,避免了一次可能很耗时的字符串复制操作。

二、右值引用
 

 右值引用的概念:

在C++中,右值引用是一种引用类型,用于绑定到右值。右值通常是临时对象或者即将销毁的值,如字面常量(如 3.14 、 'a' )、函数返回的临时对象等。右值引用使用  &&  来表示,它的出现主要是为了支持移动语义和完美转发。

1.函数返回值作为右值引用:

#include <iostream>
class MyClass {
public:
MyClass() {}
MyClass(const MyClass&) {
std::cout << "Copy constructor called." << std::endl;
}
MyClass(MyClass&&) noexcept {
std::cout << "Move constructor called." << std::endl;
}
};
MyClass getObject() {
MyClass obj;
return obj;
}
int main() {
MyClass newObj = getObject();
return 0;
}

在这个例子中, getObject 函数返回一个 MyClass 类型的临时对象。在 main 函数中, newObj 接收这个返回值。如果没有右值引用和移动语义,会调用复制构造函数来复制这个临时对象。但由于C++的编译器会识别这个临时返回值是右值,并且 MyClass 有移动构造函数,所以会调用移动构造函数,避免了不必要的复制。

2.字面常量作为右值引用

#include <iostream>
void printValue(int&& num) {
std::cout << "The value is: " << num << std::endl;
}
int main() {
printValue(10);
return 0;
}

这里定义了一个函数 printValue ,它的参数是一个右值引用 int&& num 。在 main 函数中,将字面常量 10 传递给 printValue 函数,因为 10 是右值,所以可以绑定到右值引用 num ,函数就可以使用这个右值进行操作。

使用右值引用实现移动语义

1. 实现步骤及示例代码(以自定义的资源管理类为例)

  • 步骤一:定义类和成员变量

首先,定义一个类,这个类包含需要移动的资源。假设我们创建一个简单的 String 类,它内部有一个字符指针来存储字符串内容。

class String {
private:
char* data;
size_t length;
public:
// 构造函数
String(const char* str = "") {
length = strlen(str);
data = new char[length + 1];
strcpy(data, str);
}
// 其他成员函数(如打印字符串)
void print() const {
std::cout << data << std::endl;
}
// 析构函数
~String() {
delete[] data;
}
// 移动构造函数(重点)
String(String&& other) noexcept {
// 步骤二:资源转移
data = other.data;
length = other.length;
// 步骤三:将源对象资源指针置空
other.data = nullptr;
other.length = 0;
}
// 移动赋值运算符(重点)
String& operator=(String&& other) noexcept {
if (this!= &other) {
// 释放当前对象资源
delete[] data;
// 资源转移
data = other.data;
length = other.length;
// 将源对象资源指针置空
other.data = nullptr;
other.length = 0;
}
return *this;
}
};
  • 步骤二:在移动构造函数和移动赋值运算符中进行资源转移

在移动构造函数 String(String&& other) 中,将 other 对象中的 data 指针和 length 属性赋值给新对象。这样,新对象就获得了资源的所有权。

  • 步骤三:将源对象资源指针置空或重置相关属性

在移动构造函数和移动赋值运算符完成资源转移后,需要将源对象( other )的相关资源指针置空(如 other.data = nullptr )或者重置相关属性(如 other.length = 0 ),以表明资源已经被转移,防止源对象的析构函数错误地释放已经转移的资源。

String getString() {
String temp("Hello");
return temp;
}
int main() {
String str = getString();
str.print();
return 0;
}

在 main 函数中, getString 函数返回一个临时对象 temp 。由于这个临时对象是右值,在初始化 str 对象时,会调用移动构造函数,将 temp 对象中的字符串资源转移到 str 对象中,而不是进行资源复制,提高了效率。

标签:String,右值,对象,rhs,语义,C++,移动,data
From: https://blog.csdn.net/2401_88249494/article/details/143953141

相关文章

  • 【C++学习笔记】一个先学了Java,Python,Csharp最后再来学C++的菜狗笔记
    1.字符串1.char数组charstr[]="helloworld";可以使用cstring库中的函数(如strlen,strcpy)。2.string类型#include<string>stringstr="helloworld";与csharp,java等语言不同的是动态分配内存,由标准库管理。支持操作符重载(如+,==等)。std::string是可变的,类似......
  • C++系统教程007-数据类型06(cin输入语句)
    练习:1.控制输出精确度本实例中,定义一个整型变量并赋值,定义一个双精度变量并赋值,利用cout输出这两个不同精度的格式。//控制精度#include<iostream>usingnamespacestd;intmain(){ intx=123; doubley=3.1415; cout<<"x="; cout.width(10);//设置输出域宽为10 ......
  • C++11-chrono时间库解析
    目录一、具体作用用途二、C++std::chrono时间库概述2.1、std::chrono命名空间的作用和用途2.2、基本组成部分:duration、time_point和clock三、duration的使用详解3.1、duration表示时间段的概念和使用方法3.2、duration的各种单位和精度选项3.3、使用示例四、time_p......
  • 【C++】类和对象-深度剖析默认成员函数-下
     >......
  • C++指针函数体内部初始化需要注意的地方
    有如下代码:voidchangePtr(int*p){*p=4;}intmain(){int*p=newint(5); changePtr(p);cout<<"*p:"<<*p<<endl;}以上代码我们都知道传递指针,函数改变了指针指向地址内的数据,函数体外部调用时p指向地址发生了改变,输出结果由5->4。但是在......
  • ISUP协议视频平台EasyCVR萤石设备视频接入平台应急布控球在移动执法中的应用场景有哪
    随着科技的飞速发展,安防视频监控技术已经成为现代社会不可或缺的一部分。在众多安防视频监控平台中,ISUP协议视频平台EasyCVR兼容性强、支持灵活拓展,平台可提供视频远程监控、录像、存储与回放、视频转码、视频快照、告警、云台控制、语音对讲、平台级联等视频能力。与此同时,应急......
  • VC++ 中的宏 (#define) 与预处理 (#if/#ifdef/#pragma) 的使用总结
    关键字:宏,预定义宏,预处理,预编译头,VC,#pragma,编译选项,程序区段目录C/C++预定义宏例子:C/C++预定义宏的取值C/C++预定义宏用途:诊断与调试输出CRT的诊断与调试输出:assert,_ASSERT/_ASSERTE,_RPTn/_RPTFn/_RPTWn/_RPTFWnMFC的诊断与调试输出:ASSERT/VERIFY,ASSERT_V......
  • 腾讯通RTX升级版方案:支持Linux内核国产系统及移动端
    一、腾讯通RTX继续使用的核心痛点作为国内最早一批内网即时通讯软件,腾讯通RTX在政企单位中曾占据重要地位。然而,自2015年停止更新后,不仅遗留了大量BUG,用户体验和功能也逐渐难以满足企业需求。以下是继续使用腾讯通RTX的主要痛点:●不兼容国产系统与移动端:RTX仅支持Windows和Mac,......
  • C++:类的继承
    在C++中,类的继承是一种面向对象编程(OOP)的重要特性,它允许一个类(子类或派生类)继承另一个类(基类或父类)的属性和方法。通过继承,可以重用现有的代码,减少重复代码,提高代码的可维护性和扩展性。1.基本概念基类(BaseClass或ParentClass):被继承的类。派生类(DerivedClass或Chi......
  • C++最佳情侣身高差 2024年9月c++一级 电子学会中小学生软件编程C++等级考试一级真题答
    目录C++最佳情侣身高差一、题目要求1、编程实现2、输入输出二、算法分析三、程序编写四、程序说明五、运行结果六、考点分析C++最佳情侣身高差2024年9月C++编程等级考试一级编程题一、题目要求1、编程实现专家通过多组情侣研究数据发现,最佳的情侣身高差遵循......