C++的类和动态内存分配(深拷贝与浅拷贝)并实现自己的string类
首先,我们先写一个并不完美的类:
#include<iostream>
#include<cstring>
using namespace std;class Mystring{private:char *p;int len;static int num;friend ostream& operator<<(ostream& os, const Mystring& c);public:Mystring(const char *q);Mystring(const Mystring& t);~Mystring();Mystring& operator=(const Mystring& t);
};
int Mystring::num=0;Mystring::Mystring(const char *q){len=strlen(q);p=new char[len+1];strcpy(p,q);num++;
}Mystring::~Mystring(){delete []p;p=nullptr;
}ostream& operator<<(ostream& os, const Mystring& c){os << c.p << " num: " << c.len << " all number: " << c.num;return os;
}
int main(){Mystring a("asdf");Mystring b{a};Mystring c=b;Mystring d("ghjkl");d=c;cout << a << endl << b << endl << c << endl << d;
}
大家直接看代码,可能会有一点点费劲,这里讲解一下。我们这个类有一个char*p。这个地方很关键。因为我们在构造函数中,使用了new去动态创建了一个对象。这个对象中,拥有了这个动态创建的指针。
随后,我们使用了=号。用于一个对象为另一个对象赋值,拷贝。
问题随之而来,因为我们把指针也相等的赋值了过去。这意味着,两个对象的指针指向了同一段内存,而这一段内存是我们使用动态分配new主动分配的。当其中一个对象调用了析构函数,将自己销毁掉。这个对象里面指针所指向的内存也随之释放。但是,另一个对象的指针还指向了刚才被释放掉的内存。这个时候,如果继续对剩下的对象进行销毁,调用析构函数。就会对相同的内存连续释放(delete)了两次。从而导致程序崩溃。
C++提供下面这些默认函数(如果您没有提供):
- 默认构造函数。不接受参数也不执行任何操作。
- 默认析构函数,不执行任何操作。
- 拷贝(复制)构造函数。用对象初始化另一个新建对象,逐个复制非静态成员,复制的是成员的值(浅复制)。
- 拷贝赋值运算符。用对象赋值给另一个对象,逐个复制非静态成员,复制的是成员的值(浅复制)。
- 移动构造函数。C++11增加。
- 移动赋值运算符。C++11增加。
- 地址运算符。返回对象的地址。和我们想象一致,不再讨论。
使用默认拷贝构造函数和使用默认=赋值运算符,导致浅拷贝,在析构时会出现重复释放(delete)同一段内存,导致程序崩溃。
怎么解决这个问题呢?
1、自己定义拷贝构造函数和重载=号,实现深拷贝。
如下,我们自己实现了重载=和自定义拷贝构造函数。(这也是为什么,一旦类中出现了指针,就需要自己定义=和拷贝构造函数,因为一定会出现浅拷贝)只有自己实现了这些功能,才会实现深拷贝。
#include<iostream>
#include<cstring>
using namespace std;class Mystring{private:char *p;int len;static int num;friend ostream& operator<<(ostream& os, const Mystring& c);public:Mystring(const char *q);Mystring(const Mystring& t);~Mystring();Mystring& operator=(const Mystring& t);
};
int Mystring::num=0;
Mystring::Mystring(const char *q){len=strlen(q);p=new char[len+1];strcpy(p,q);num++;
}
//重新写的拷贝构造函数
Mystring::Mystring(const Mystring& t){p=new char[t.len+1];len=t.len;strcpy(p,t.p);num++;
}
Mystring::~Mystring(){delete []p;p=nullptr;
}
//重新写的=号的重载
Mystring& Mystring::operator=(const Mystring& t){delete[]p;p=new char[t.len+1];strcpy(p,t.p);len=strlen(p);return *this;
}
ostream& operator<<(ostream& os, const Mystring& c){os << c.p << " num: " << c.len << " all number: " << c.num;return os;
}
int main(){Mystring a("asdf");Mystring b{a};Mystring c=b;Mystring d("ghjkl");d=c;cout << a << endl << b << endl << c << endl << d;
}
浅拷贝:顾名思义,就是单纯的把值传了过去。但是因为涉及到了动态分配的内存,导致两个指针指向了同一个地方,最终释放的时候,会出现同一段内存被多次释放。最后崩溃掉。
而深拷贝:就是使用new重新再分配一段内存,分给拷贝的对象,不让两个指针指向同一段内存,只有他们各自指向了一段内存,才不会被多次释放。
重新写拷贝构造和重载=号,实际上就是多了一步new(分配内存)的步骤。代码如上,可以复制看看。
String s2(s1);String s3 = s1;string s4 = string(s1);string* ps4 = new string(s1);//1.调用拷贝构造函数//2.调用拷贝构造函数//3.调用拷贝构造函数//4.调用拷贝构造函数
如上,是四种拷贝函数的用法。都是调用了拷贝构造函数。区别在于,有的是临时对象,有的是直接构造,有的是动态分配了一个,然后构造。
这里说一个重点,如果有动态分配的内存,那么new和delete在构造和析构中,一定要一一对应。
如果,一个类中,出现了指针并且涉及到了动态内存分布,我们就必须要重新写 析构函数 拷贝构造函数 拷贝赋值 (移动拷贝构造) (移动拷贝赋值)
如果对象中出现指针成员变量,那么必须实现构造函数、析构函数、拷贝构造函数和=重载函数,以达到深拷贝。
每日金句:
一切所谓不可能之事,其实都是尚未发生的事。
-----------黄泉
相关文章:

C++的类和动态内存分配(深拷贝与浅拷贝)并实现自己的string类
首先,我们先写一个并不完美的类: #include<iostream> #include<cstring> using namespace std;class Mystring{private:char *p;int len;static int num;friend ostream& operator<<(ostream& os, const Mystring& c);pu…...

通过观测云 DataKit Extension 接入 AWS Lambda 最佳实践
前言 AWS Lambda 是一项计算服务,使用时无需预配置或管理服务器即可运行代码。AWS Lambda 只在需要时执行代码并自动缩放。借助 AWS Lambda,几乎可以为任何类型的应用程序或后端服务运行代码,而且无需执行任何管理。 Lambda Layer 是一个包…...

MySQL-三范式 视图
文章目录 三范式三范式简介第一范式第二范式第三范式 表设计一对一一对多多对多最终的设计 视图 三范式 三范式简介 所谓三范式, 其实是表设计的三大原则, 目的都是为了节省空间, 但是三范式是必须要遵守的吗? 答案是否定的(但是第一范式必须遵守) 因为有时候严格遵守三范式…...

多线程(三):线程等待获取线程引用线程休眠线程状态
目录 1、等待一个线程:join 1.1 join() 1.2 join(long millis)——"超时时间" 1.3 join(long millis,int nanos) 2、获取当前线程的引用:currentThread 3、休眠当前进程:sleep 3.1 实际休眠时间 3.2 sleep的特殊…...

Hi3244 应用指导
Hi3244 是一款DIP8封装高性能、多模式工作的原边控制功率开关。Hi3244内高精度的恒流、恒压控制机制结合完备的保护功能,使其适用于小功率离线式电源应用中。在恒压输出模式中,Hi3244 采用多模式工作方式,即调幅控制(AM࿰…...
【LeetCode热题100】哈希
1.两数之和 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。 你可以按任意顺序返回答…...
Java的四种循环语句
背景: Java 中主要有四种循环语句:for 循环、while 循环、do-while 循环 和 foreach 循环(也称为增强型 for 循环)。下面我将分别介绍这四种循环语句,并给出相应的实例。 for循环: 1. for 循环for 循环是…...
Qt杂记目录
Qt 杂记目录 QMenu 1.menu转string Qt 窗口阴影边框...
项目开发--基于docker实现模型容器化服务
背景 1、docker-compose build 和 docker-compose up -d分别是什么作用? 2、如何进入新构建的容器当中 3、模型保存的方法区别 4、如何让docker容器启动的时候能使用cuda进行模型推理加速 5、如何实现容器的迭代 解决方案 问题1 docker-compose build 和 docker…...

C语言 | Leetcode C语言题解之第477题汉明距离总和
题目: 题解: int totalHammingDistance(int* nums, int numsSize) {int ans 0;for (int i 0; i < 30; i) {int c 0;for (int j 0; j < numsSize; j) {c (nums[j] >> i) & 1;}ans c * (numsSize - c);}return ans; }...
Bug剖析
Bug剖析 • 所有的Bug报告有以下的基本要求: • 标题。要简略。 • 指派。谁来处理这个问题。 • 重现步骤。问题再次出现的相关步骤。 • 优先级别。问题的紧迫性与重要性。 • 严重程度。问题所产生的后果。 • 解决方案。怎么解决问题。 其他很多方面对修复问题…...

HI3516DV500 相机部分架构初探
Hi3516DV500 是一颗面向视觉行业推出的高清智能 Soc。该芯片最高支持 2 路 sensor 输入,支持最高 5M30fps 的 ISP 图像处理能力,支持 2F WDR、多级降噪、六轴防 抖、多光谱融合等多种传统图像增强和处理算法,支持通过 AI 算法对输入图像进行实…...
训练yolo系列出现问题mAP, R, P等为零
1. 问题 40系列显卡训练yolo系列出现问题,loss正常,但mAP,R,P等为零。 环境:ultralytics版本为8.3.9,cuda11.8, torch2.4。 40系列显卡网上说可以使用cuda低于11.7的,自己测试了下…...

数字媒体技术基础:色度子采样(4:4:4、4:2:2 、4:2:0)
在数字视频处理中,色度子采样 Chroma Subsampling可以用于压缩视频文件的大小,同时在大多数情况下保持较高的视觉质量,它的原理基于人类视觉系统对亮度 Luminance比对色度 Chrominance更加敏感这一特点。 一、 采样格式的表示方法 色度子采样…...

tkinter库的应用小示例:文本编辑器
tkinter库的应用小示例:文本编辑器 要 求: 创建一个文本编辑器,功能包括,创建、打开、编辑、保存文件。一个Button小组件,命名为btn_open,用于打开要编辑的文件,一个Button小组件,命名为btn_s…...
信息抽取数据集处理——RAMS
引言 RAMS数据集(RAMS:Richly Annotated Multilingual Schema-guided Event Structure)由约翰斯霍普金斯大学于2020年发布,是一个以新闻为基础的事件抽取数据集。它标注了9,124个事件,涵盖了139种不同的事件类型和65种…...

SpringBoot+XXL-JOB:高效定时任务管理
前言 在现代应用程序中,定时任务是不可或缺的一部分。Spring Boot 和 XXL-Job 为你提供了一个强大的工具组合,以简化任务调度和管理。 本文将带领你探索如何将这两者集成在一起,实现高效的定时任务管理。无论你是初学者还是有经验的开发者&…...
openpyxl -- 简介
文章目录 介绍核心类 介绍 开源的python读写Excel的工具库,由志愿者在业余时间维护;安装,pip install openpyxl;官方文档地址源码地址issues列表默认openpyxl不能保证抵御大量的xml攻击,为抵御这些攻击需安装defusedxmlÿ…...
滚雪球学MySQL[8.3讲]:数据库中的JSON与全文检索详解:从数据存储到全文索引的高效使用
全文目录: 前言8.3 JSON与全文检索1. JSON数据类型的使用1.1 JSON 数据类型概述1.2 JSON 数据的插入与查询1.3 JSON 常用函数与操作1.4 JSON使用的优缺点与性能考虑 2. 全文索引与全文检索2.1 全文索引概述2.2 全文检索的使用2.3 全文检索模式2.4 全文索引优化与性能…...

position定位静态定位/绝对定位/相对定位
1.静态定位static:按照标准流进行布局 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>D…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...

什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...

ubuntu22.04有线网络无法连接,图标也没了
今天突然无法有线网络无法连接任何设备,并且图标都没了 错误案例 往上一顿搜索,试了很多博客都不行,比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动,重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...