C++之深拷贝进阶

目录

拷贝构造函数的深拷贝进阶版本

赋值运算符重载的深拷贝进阶

总结


上期我们学习了C++中深拷贝的传统版本,今天我们将学习更为高效的版本。

拷贝构造函数的深拷贝进阶版本

传统版本代码如下:

string(string& s):_str(new char[strlen(s._str) + 1])
{strcpy(_str, s._str);
}

进阶版本代码如下:

string(string& s):_str(nullptr)
{string tmp(s._str);std::swap(_str, tmp._str);
}

注意:因为是初始化一个刚创建的对象,我们一定要哦将_str置空,否则其指向了一个未知的空间,等到刚创建的对象的_str成员变量和tmp对象的成员变量_str交换之后,因为未知的空间不是new出来的,所以无法进行释放,也就就会导致tmp._str在最终调用析构函数时会出错。 

图示如下:

解析:不难发现,进阶版本相比较于传统版本,进阶版本并没有像传统版本一样,申请空间之后调用和strcpy函数去进行字符串的拷贝,而是调用构造函数创建并初始化了一个对象tmp, 最终交换了成员变量的指针,从而使新创建的对象的成员指针指向了tmp._str指向的空间。

运行截图如下:

赋值运算符重载的深拷贝进阶

传统版本代码如下:

string& operator=(string& s)
{if (this != &s){char* tmp = new char[strlen(s._str) + 1];strcpy(tmp, s._str);delete[]_str;_str = tmp;}return *this;}

进阶版本代码如下:

进阶版本1:

string& operator =(string &s){//为了避免自己给自己进行赋值if (this != &s){string tmp(s);std::swap(_str, tmp._str);}return *this;}

解析:进阶版本的区别是调用拷贝构造函数初始化了一个对象tmp,然后对这tmp对象和我们要进行赋值的对象的成员变量_str进行了交换,最终完成了赋值。 

 进阶版本2:

string& operator =(string s)
{std::swap(_str, s._str);return *this;
}

版本2和版本1的区别是什么?

版本1要考虑是否给自己赋值,但是版本2避开了这种情况,版本2的形参就是一个中间变量,我们通过拷贝构造函数用实参对其进行了初始化,此时的形参s和被赋值的对象根部就不可能是同一个对象,但是第1个版本的形参是引用类型,所以就会存在自身给自身赋值的情况。 

图示如下:

解析:通过图示我们可以看出来,赋值运算符重载的深拷贝的进阶版本实质上也是创建了一个中间对象,最终通过交换函数实现了两个对象的成员变量_str的交换从而实现了赋值,与拷贝构造函数的深拷贝的进阶版本不同的是,赋值运算符重载是针对两个已经存在的对象进行赋值,两个对象的指针交换之后,最终都会调用各自的析构函数完成资源的清理。 

运行截图如下: 

解析:说白了,不管是拷贝构造函数的进阶版本还是赋值运算符重载的进阶版本,其实本本质都是对函数的复用,拷贝构造函数深拷贝的进阶在创建对象时复用了构造函数。赋值运算符重载的深拷贝进阶复用了拷贝构造函数的深拷贝进阶,因为拷贝构造函数的深拷贝进阶又复用了构造函数,所以本质上,拷贝构造函数的赋值运算深拷贝进阶和符重载的深拷贝进阶都是复用了构造函数。 

总结

到了这里我们自己创建的string类的深拷贝和浅拷贝的全部知识已经全部学习完成了,目前的string类整个代码为:

namespace yjd
{class string{public://构造函数string(const char* str):_str(new char[strlen(str) + 1]){strcpy(_str, str);}//拷贝构造函数的深拷贝传统版本string(string& s):_str(new char[strlen(s._str) + 1]){strcpy(_str, s._str);}//赋值运算符重载的深拷贝传统版本string& operator=(string& s){if (this != &s){char* tmp = new char[strlen(s._str) + 1];strcpy(tmp, s._str);delete[]_str;_str = tmp;}return *this;}//拷贝构造函数的深拷贝进阶版本string(string& s):_str(nullptr){string tmp(s._str);std::swap(_str, tmp._str);}//赋值运算符重载的深拷贝进阶版本1string& operator =(string &s){//为了避免自己给自己进行赋值if (this != &s){string tmp(s);std::swap(_str, tmp._str);}return *this;}//赋值运算符重载的深拷贝进阶版本2string& operator =(string s){std::swap(_str, s._str);return *this;}~string(){delete [] _str;_str = nullptr;}private:char* _str;};
}

注意:以上整体代码有两个个需要注意的地方:

1.首先我们自定义了命名空间,不然就会和库中的string类产生冲突。

2.构造函数的形参的类型为const char*,因为const char*不仅可以接收常量字符串的地址,也可以接收字符数组类型的字符串地址,因为权限不变和权限缩小是可以的,而char*只可以接收数组类型的字符串,因为权限放大是不允许的,string类的字符串我们就可以定性为数组字符串,本质上就是一个数组。

本期内容到此结束^_^ 

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

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

相关文章

Mybatis3系列课程8-带参数查询

简介 上节课内容中讲解了查询全部, 不需要带条件查, 这节我们讲讲 带条件查询 目标 1. 带一个条件查询-基本数据类型 2.带两个条件查询-连个基本数据类型 3.带一个对象类型查询 为了实现目标, 我们要实现 按照主键 查询某个学生信息, 按照姓名和年级编号查询学生信息 按照学生…

C语言中的关键字

Static 静态局部变量 结果: a作为静态局部变量,第一次进入该函数的时候,进行第一次变量的初始化,在程序整个运行期间都不释放。(因为下一次调用还继续使用上次调用结束的数值) 但是其作用域为局部作用域&…

深入理解 JavaScript 函数:提升编程技能的必备知识(中)

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…

基于kubernetes实现PaaS云平台-rancher

基于Rancher实现kubernetes集群管理 一、Rancher介绍 1.1 Rancher Rancher 是一套容器管理平台,它可以帮助组织在生产环境中轻松快捷的部署和管理容器。Rancher可以轻松地管理各种环境的 Kubernetes,满足IT需求并为 DevOps 团队提供支持。 Rancher 用…

JUC并发编程 08——原子操作类

目录 一.原子更新基本类型类 实现原理 二.原子更新数组 三.原子更新引用类型 四.原子更新字段类 Java从JDK1.5开始提供了J.U.C下的atomic包,atomic包提供了一系列的操作简单,性能高效,并能保证线程安全的类去更新基本类型变量&#xff0…

数字图像处理-空间域图像增强-爆肝18小时用通俗语言进行超详细的总结

目录 灰度变换 直方图(Histogram) 直方图均衡 直方图匹配(规定化) 空间滤波 低通滤波器 高通滤波器 ​​​​​​​ 本文章讲解数字图像处理空间域图像增强,大部分内容来源于课堂笔记中 灰度变换 图像增强&…

C++的面向对象学习(4):对象的重要特性:构造函数与析构函数

文章目录 前言:将定义的类放在不同文件夹供主文件调用的方法一、构造函数与析构函数1.什么是构造函数和析构函数?2.构造函数和析构函数的语法3.构造函数的具体分类和调用方法①总的来说,构造函数分类为:默认无参构造、有参构造、拷…

【扩散模型】9、Imagen | 借用语言模型的能力来实现文生图(NIPS2022 Oral)

文章目录 一、背景二、方法2.1 预训练的语言编码器2.2 扩散模型和 classifier-free guidance 三、效果 论文:Imagen: Photorealistic Text-to-Image Diffusion Models with Deep Language Understanding 官网:https://www.assemblyai.com/blog/how-imag…

Python命名规范中的[单/双][前导/后缀]下划线小结

如图所示 出处 Single and Double Underscores in Python Names

flutter + firebase 云消息通知教程 (android-安卓、ios-苹果)

如果能看到这篇文章的 一定已经对手机端的 消息推送通知 有了一定了解。 国内安卓厂商这里不提都有自己的FCM 可自行查找。(国内因无法科学原因 ,不能使用谷歌服务)只说海外的。 目前 adnroid 和 ios 推送消息分别叫 FCM 和 APNs。这里通过…

金蝶云星空业务对象添加网控设置

文章目录 金蝶云星空业务对象添加网控设置排查是否已经网控设置网控设置 金蝶云星空业务对象添加网控设置 排查是否已经网控设置 网控设置

TrustZone之安全启动与引导失败处理

一、引导和信任链 引导是任何TrustZone系统的关键部分。只有在引导流程中之前运行的所有软件组件都是可信的情况下,才能信任某个软件组件。这通常被称为信任链。下图显示了一个简化的信任链: 在我们的示例中,首先运行的代码是boot ROM。我们必须隐式信任boot ROM,因…