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…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...
OCR MLLM Evaluation
为什么需要评测体系?——背景与矛盾 能干的事: 看清楚发票、身份证上的字(准确率>90%),速度飞快(眨眼间完成)。干不了的事: 碰到复杂表格(合并单元…...
DeepSeek越强,Kimi越慌?
被DeepSeek吊打的Kimi,还有多少人在用? 去年,月之暗面创始人杨植麟别提有多风光了。90后清华学霸,国产大模型六小虎之一,手握十几亿美金的融资。旗下的AI助手Kimi烧钱如流水,单月光是投流就花费2个亿。 疯…...
