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

C++学习笔记----8、掌握类与对象(一)---- 对象中的动态内存分配(5)

2.4.2、c++23 decay copy

        如果有一个对象x,“auto y{x}”生成一个x的拷贝并且给它一个名字y;这样,它是一个左值。

c++23引入了auto(x)或者auto{x}的语法来生成一个对象x的拷贝作为右值,而不是左值。

        作为一个例子,假定前面介绍的handleMessage(string&&)函数只有右值引用,而没有左值引用重载。你应该清楚在这种情况下下面的代码是不灵的:

string value { "Hello " };
handleMessage(value); // Error

        可以使用std::move(),如下:

handleMessage(std::move(value));

        但是,这个操作之后,就不能再使用value对象了,因为它可能已经被移走了。

        使用C++23 decay-copy语法,可以写成:

handleMessage(auto { value });

        这使得value对象的临时拷贝作为一个右值并且将这个右值传递给handleMessage()。如果handleMessage()从该拷贝移走,原来的对象value保持原状不受影响。

2.4.3、实现move语法

        move语法使用右值引用来实现。给一个类增加Move语法,需要实现move构造函数和move赋值操作符。move构造函数与Move赋值操作符应该标记为noexcept告诉编译器它们不会抛出例外。这对于标准库的兼容性特别重要,作为完全兼容的实现,例如,标准库容器如果只移动保存的对象,实现move语法的话,它们也保证不会抛出例外。这么做是为了能够提供强大的例外安全。

        下面是带有move构造函数与Movem赋值操作符的Spreadsheet类的定义。两个辅助成员函数也进行了介绍:cleanup(),在析构函数与Movem赋值操作符中使用,以及moveFrom(),它从源移动数据成员到目标,然后重置源对象。

export class Spreadsheet
{
public:Spreadsheet(Spreadsheet&& src) noexcept; // Move constructorSpreadsheet& operator=(Spreadsheet&& rhs) noexcept;  // Move assignment// Remaining code omitted for brevity
private:void cleanup() noexcept;void moveFrom(Spreadsheet& src) noexcept;// Remaining code omitted for brevity
};

        实现如下:

void Spreadsheet::cleanup() noexcept
{for (size_t i{ 0 }; i < m_width; ++i) {delete[] m_cells[i];}delete[] m_cells;m_cells = nullptr;m_width = m_height = 0;
}void Spreadsheet::moveFrom(Spreadsheet& src) noexcept
{// Shallow copy of datam_width = src.m_width;m_height = src.m_height;m_cells = src.m_cells;// Reset the source object, because ownership has been moved!src.m_width = 0;src.m_height = 0;src.m_cells = nullptr;
}// Move constructor
Spreadsheet::Spreadsheet(Spreadsheet&& src) noexcept
{println("Move constructor");moveFrom(src);
}// Move assignment operator
Spreadsheet& Spreadsheet::operator=(Spreadsheet&& rhs) noexcept
{println("Move assignment operator");// check for self-assignmentif (this == &rhs) {return *this;}// Free the old memory and move ownershipcleanup();moveFrom(rhs);return *this;
}

        move构造函数与move赋值操作符移动m_cells的内存属主从源对象到一个新的对象。重置源对象的m_cells指针为null指针并且 设置源对象的m_width和m_height为0以你看期间源对象的析构函数释放内存,因为新的对象现在已成为其属主。

        很明显,Move语法只有在你知道源对象不再需要时才有用。

        注意这个实现里包含了一个在Move赋值操作符中的自我赋值的检测。依赖于你的类并且依赖于你怎么将类的一个实例移动到另一个实例,该自我检测可能并不问题需要。然而,你还是应该包含它,就像我们的c++核心指导推荐中所说的那样,要保证如下代码不会在运行时产生崩溃:

sheet1 = std::move(sheet1);

        move构造函数与move赋值操作符可以显式删除或缺省,与拷贝构造函数和拷贝赋值操作符一样。

        编译器在并且只有在类没有用户声明的拷贝构造函数,拷贝赋值操作符,move赋值操作符,或者析构函数的情况下才会自动为类生成一个缺省的move构造函数。在且只有在类没有用户声明的拷贝构造函数,move构造函数,拷贝赋值操作符,或者析构函数的情况下才会为类生成一个缺省的move赋值操作符。

        警告:当你声明一个或多个特殊成员函数(析构函数,拷贝构造函数,move构造函数,拷贝赋值操作符,以及move赋值操作符)时,推荐全部声明这些函数,这被叫做五规则。或者提供显式地实现,或者显式地缺省(=default)或删除(=delete)。

2.4.4、使用std::exchange

可以使用<utility>中定义的std::exchange,来用一个新值替换并且返回旧值,示例如下:

import std;using namespace std;int main()
{int a{ 11 };int b{ 22 };println("Before exchange(): a = {}, b = {}", a, b);int returnedValue{ exchange(a, b) };println("After exchange():  a = {}, b = {}", a, b);println("exchange() returned: {}", returnedValue);
}

        输出如下:

Before exchange(): a = 11, b = 22
After exchange(): a = 22, b = 22
exchange() returned: 11

        exchange()函数在实现move赋值操作符时很有用。move赋值操作符需要将数据从源对象移到目标对象,这之后在源对象中的数据通常会被置成Null。前面是通过下面的代码来实现的:

void Spreadsheet::moveFrom(Spreadsheet& src) noexcept
{// Shallow copy of datam_width = src.m_width;m_height = src.m_height;m_cells = src.m_cells;// Reset the source object, because ownership has been moved!src.m_width = 0;src.m_height = 0;src.m_cells = nullptr;
}

        该成员函数从源对象拷贝m_width,m_height,和m_cells数据成员,然后将其设置成0或者nullptr,因为属主发生了转移。使用exchange()可以将代码写得更紧凑,如下:

void Spreadsheet::moveFrom(Spreadsheet& src) noexcept
{m_width = exchange(src.m_width, 0);m_height = exchange(src.m_height, 0);m_cells = exchange(src.m_cells, nullptr);
}

相关文章:

  • 1panel申请https/ssl证书自动续期
  • Kafka系列之:安装部署CMAK,CMAK管理大型Kafka集群参数调优
  • 微软Win11 22H2/23H2 九月可选更新KB5043145发布!
  • Mitsuba 渲染基础
  • 如何使用C语言接入Doris数据库
  • 【Linux服务器】git和github交互使用
  • docker pull镜像失败问题解决尝试
  • 极狐GitLab 17.4 重点功能解读【九】
  • 云计算课程作业1
  • TS系列(4):常用类型之类、抽象类和接口
  • Steam黑神话悟空禁止更新进入游戏的解决方案
  • 【开源免费】基于SpringBoot+Vue.JS技术交流分享平台(JAVA毕业设计)
  • 多线程相关内容
  • 图解FTP服务器配置:实体用户方式访问案例
  • SQL CREATE TABLE 语句
  • 网络传输文件的问题
  • 【剑指offer】让抽象问题具体化
  • ES6 ...操作符
  • Java,console输出实时的转向GUI textbox
  • node入门
  • 闭包--闭包作用之保存(一)
  • - 概述 - 《设计模式(极简c++版)》
  • 前端面试之闭包
  • 前端自动化解决方案
  • 扫描识别控件Dynamic Web TWAIN v12.2发布,改进SSL证书
  • hi-nginx-1.3.4编译安装
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ​Java基础复习笔记 第16章:网络编程
  • # linux从入门到精通(三)
  • #宝哥教你#查看jquery绑定的事件函数
  • $jQuery 重写Alert样式方法
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (八)c52学习之旅-中断实验
  • (二)换源+apt-get基础配置+搜狗拼音
  • (附源码)ssm高校实验室 毕业设计 800008
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (转) Face-Resources
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • (转)jQuery 基础
  • (转)mysql使用Navicat 导出和导入数据库
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .net CHARTING图表控件下载地址
  • .net core 的缓存方案
  • .NET Framework与.NET Framework SDK有什么不同?
  • .Net Memory Profiler的使用举例
  • .NET(C#) Internals: as a developer, .net framework in my eyes
  • @RestController注解的使用
  • [ CTF ] WriteUp- 2022年第三届“网鼎杯”网络安全大赛(朱雀组)
  • [20161101]rman备份与数据文件变化7.txt
  • [BZOJ1178][Apio2009]CONVENTION会议中心
  • [bzoj2957]楼房重建
  • [C# WPF] DataGrid选中行或选中单元格的背景和字体颜色修改
  • [C++ 从入门到精通] 12.重载运算符、赋值运算符重载、析构函数
  • [Design Pattern] 工厂方法模式