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

C++之深拷贝和浅拷贝

目录

浅拷贝

深拷贝

赋值运算符重载的深拷贝


在学习C++类和对象时我们学习了浅拷贝,本期我们将再次回顾浅拷贝并为大家讲述深拷贝的概念。

浅拷贝

在学习类和对象时我们学习了拷贝构造函数的概念,而且我们也知道,因为拷贝构造函数属于类的默认成员函数吗,所以当我们没有显示定义拷贝构造函数时,编译器会生成拷贝构造函数,编译器生成的拷贝构造函数对内置类型成员变量会完成字节序的值拷贝,对于自定义类型会去调用其拷贝构造函数。这里对内置类型成员变量进行的字节序值拷贝我们就称作浅拷贝,为什么我们我们要称字节序的值拷贝为浅拷贝呢?

我们通过依然自定义的string类中的拷贝构造函数为大家讲解:

代码如下:

class string{public:string(const char* str):_str(new char[strlen(str) + 1]){strcpy(_str, str);}~string(){delete [] _str;_str = nullptr;}private:char* _str;};void test1(){string s1("hello yjd");//调用拷贝构造函数string s2(s1);//调用赋值运算符重载string s3("hello world");s3 = s1;}

如上代码,我们自己定义了一个string类,string类由于我们没有声明拷贝构造函数,所以编译器会生成默认的拷贝构造函数,但是生成的默认的拷贝构造函数会对内置类型进行值拷贝,就会将s1对象的_str成员变量的值传给s2对象的_str成员变量。

所以就会导致下面这种情况,图示如下: 

运行截图如下:

不难发现,编译器提示我们出现了错误,这是因为编译器默认生成的拷贝构造函数对于内置类型是值拷贝,所以s1._str的值会赋给s2._str,就会导致两个指针变量指向了统一块动态申请的空间,既然是动态申请的空间在最后为了防止内存泄漏肯定是要使释放的,因为两个对象在小销毁之前都会调用析构函数进行资源的清理,所以这就会导致统一块空间被释放了两次,这边会导致错误,所以为了防止这种情况的出现,在此情境下诞生了深拷贝。 

深拷贝

什么是深拷贝,说白了深拷贝就是为了解决在堆上申请的空间被释放两次的问题,针对上述浅拷贝所产生的现象,为了防止两个指针指向了同一块空间,我们可以先让s2在堆上申请与s1大小相同的空间,在C语言中我们学习了字符串函数strcpy,我们可以通过这个函数实现两个字符串值的传递。

图示如下:

通过图示,不难发现,两个对象的成员变量指向了两个不同的空间,所以此时我们也就不用担心在堆区开辟的空间被释放两次的问题,所以深拷贝就解决了浅拷贝中所带来的不足。

代码如下:

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

上述代码便是我们自己生成的拷贝构造函数代码,所以在今后学的的数据结构只要需要在堆上动态开辟空间,我们就需要自己定义拷贝构造函数。

赋值运算符重载的深拷贝

在学习类和对象时我们也学习了赋值运算符重载,赋值运算符重载和拷贝构造函数的区别是:

1.拷贝构造函数是用一个已经存在的对象初始化一个刚刚创建的对象。

2.赋值运算符重载是两个已经存在的对象之间进行赋值。

图示如下:

解析:因为赋值运算符重载也属于类的成员函数,当我们没有显示定义赋值运算符重载时,编译器也会默认生成一个赋值运算符重载,但是默认生成的赋值运算符重载和默认生成的拷贝构造函数是类似的,对于内置类型的成员变量,会完成字节序的值拷贝(浅拷贝),通过默认拷贝构造函数我们知道,浅拷贝是存在弊端的,所以对于在堆上开辟空间的数据结构,我们必须自己显示定义赋值运算符重载。

        其实整个过程和拷贝构造函数类似,不过赋值运算符重载我们要先释放掉原来的空间,释放掉原来的空间之后,在动态开辟一块与s1大小相同的空间,然后将这块空间的地址传给s3._str,然后通过strcpy函数实现字符串值的拷贝。

代码如下:

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.自己不需要给自己赋值,所以直接返回自身即可。

2.为了避免申请空间失败仍然释放掉原来空间的这种情况,我们定义了一个中间变量tmp,当空间申请失败时,我们则不释放原来的空间,当空间申请成功时,我们先赋值给临时字符串tmp,最后再将临时字符串赋值给成员变量_str就可以完成赋值运算符重载。

好了,深拷贝和浅拷贝的内容到这里就全部结束了,我们本次为大家带来的是传统版本的深拷贝,后期会为大家讲述效率更高的的深拷贝的方法。 

本期内容到此结束^_^

相关文章:

  • 测试理论知识四:大型软件的测试技巧——单元测试
  • DotNet Core 在Centos上使用环境变量运行 urls 参数
  • 《每天一分钟学习C语言·七》指针、字节对齐等
  • 只更新软件,座椅为何能获得加热功能?——一文读懂OTA
  • npm的使用技巧
  • UE5 Landscape 制作GIS卫星图地形
  • docker的应用和定义
  • [CVPR 2023:3D Gaussian Splatting:实时的神经场渲染]
  • RTP/RTCP/RTSP/SIP/SDP/RTMP对比
  • 10 Vue3中v-html指令的用法
  • 【Matlab in VSCode】在VSCode中编辑MATLAB文件
  • Win7如何修改MAC地址
  • 油猴脚本教程案例【键盘监听】-编写 ChatGPT 快捷键优化
  • 【原理图PCB专题】原理图图纸锁定/解锁与PCB文件加密方式
  • C语言操作符详解+运算符优先级表格
  • ECMAScript 6 学习之路 ( 四 ) String 字符串扩展
  • HTML-表单
  • iOS 系统授权开发
  • java 多线程基础, 我觉得还是有必要看看的
  • java取消线程实例
  • js算法-归并排序(merge_sort)
  • Laravel Telescope:优雅的应用调试工具
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • Promise面试题2实现异步串行执行
  • Python socket服务器端、客户端传送信息
  • Spark RDD学习: aggregate函数
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • spring boot下thymeleaf全局静态变量配置
  • vue.js框架原理浅析
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • Vue2.x学习三:事件处理生命周期钩子
  • windows下mongoDB的环境配置
  • 从伪并行的 Python 多线程说起
  • 基于Android乐音识别(2)
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 微服务入门【系列视频课程】
  • 用简单代码看卷积组块发展
  • 找一份好的前端工作,起点很重要
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • !! 2.对十份论文和报告中的关于OpenCV和Android NDK开发的总结
  • #includecmath
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (C#)if (this == null)?你在逗我,this 怎么可能为 null!用 IL 编译和反编译看穿一切
  • (python)数据结构---字典
  • (附源码)spring boot基于小程序酒店疫情系统 毕业设计 091931
  • (附源码)ssm捐赠救助系统 毕业设计 060945
  • (附源码)计算机毕业设计大学生兼职系统
  • (论文阅读23/100)Hierarchical Convolutional Features for Visual Tracking
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • *1 计算机基础和操作系统基础及几大协议
  • .mkp勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET 4.0网络开发入门之旅-- 我在“网” 中央(下)
  • .net core Swagger 过滤部分Api
  • .NET Standard、.NET Framework 、.NET Core三者的关系与区别?