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…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
如何配置一个sql server使得其它用户可以通过excel odbc获取数据
要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据,你需要完成以下配置步骤: ✅ 一、在 SQL Server 端配置(服务器设置) 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到:SQL Server 网络配…...
6.9-QT模拟计算器
源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...
