16.C++STL 3(string类的模拟,深浅拷贝问题)
⭐本篇重点:string类的模拟,自己实现一个简单的string类
⭐本篇代码:c++学习/05.string类的学习 · 橘子真甜/c++-learning-of-yzc - 码云 - 开源中国 (gitee.com)
目录
一. 经典string类的模拟
1.1 深浅拷贝问题
1.2 使用深拷贝完成经典string类的模拟
a size函数
b 拷贝构造函数
c 赋值运算符重载
d operator[]重载
二. 现代写法的string类模拟
2.1 拷贝构造函数
2.2 赋值运算符重载
三. 下篇文章:STL中vector的使用
一. 经典string类的模拟
实现一个简单的string类,主要是实现string类的构造函数,析构函数,拷贝构造函数,赋值运算符重载。这个过程需要我们首先理解深浅拷贝的问题。
1.1 深浅拷贝问题
浅拷贝问题:如果我们初始化一个对象的时候,只是简单的将另一个对象的值赋值给这个对象。比如我们在堆上申请的空间。当我们销毁这两个对象的时候,由于它们指向同一个空间,这个空间就会被销毁两次。程序就会崩溃。
测试代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;namespace yzc
{class string{public:string(const char* str = ""):_str(new char[strlen(str) + 1]){cout << "调用构造函数" << endl;strcpy(_str, str);}//拷贝构造string(const string& str){*this = str;}~string(){cout << "调用析构函数" << endl;delete[] _str;}void print(){cout << _str << endl;}private:char* _str;};
}int main()
{yzc::string s1 = "123456";yzc::string s2 = "abcdef";s1.print();s2.print();return 0;
}
没有调用拷贝构造函数,可以正常输出。
运行结果如下:

如果我们使用拷贝构造函数
int main()
{yzc::string s1 = "123456";yzc::string s2(s1);return 0;
}
分析代码可知,由于浅拷贝问题。输出一次调用构造函数和两次析构函数后程序崩溃
只有使用深拷贝,我们在拷贝构造函数里面重新申请一份空间,然后重新全部复制才行。

1.2 使用深拷贝完成经典string类的模拟
a size函数
为了完成深拷贝,我们需要定义一个size函数用于求_str的长度。加上const,因为函数内部并不会更改任何值
int size()const{return strlen(_str);}
b 拷贝构造函数
与构造函数类似,我们重新开辟一份空间,然后使用strcpy进行拷贝
//拷贝构造,使用深拷贝完成拷贝string(const string& s):_str(new char[s.size()]){strcpy(_str, s._str);}
测试代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;namespace yzc
{class string{public:string(const char* str = ""):_str(new char[strlen(str) + 1]) //由于C语言字符串后面都带'\0',需要加上1{strcpy(_str, str);}拷贝构造//string(const string& str)//{// *this = str;//}//拷贝构造,使用深拷贝完成拷贝string(const string& s):_str(new char[s.size() + 1]) //一定要注意要加1,否则会越界访问{strcpy(_str, s._str);}~string(){delete[] _str;_str = nullptr;}int size()const{return strlen(_str);}void print(){cout << _str << endl;}private:char* _str;};
}int main()
{yzc::string s1 = "123456";yzc::string s2(s1);s1.print();s2.print();return 0;
}
测试结果

c 赋值运算符重载
与拷贝构造函数一样,要注意深浅拷贝的问题
//赋值运算符重载string& operator=(const string& s){if (this != &s)//地址不同才进行赋值{delete[] _str; //释放原地址空间,防止内存泄漏//定义中间变量tmp用于拷贝char* tmp = new char[s.size() + 1]; //注意要 + 1strcpy(tmp, s._str);_str = tmp;}return *this; //返回当前对象,为了支持 a = b = c}
测试:主函数代码如下
int main()
{yzc::string s1 = "123456";yzc::string s2 = "abcdef";yzc::string s3 = s1;yzc::string s4;s4 = s2;s3.print();s4.print();return 0;
}
测试结果:

d operator[]重载
我们还能重载[]这个操作符,方便我们遍历整个字符串
直接输入i,返回_str[i]即可(注意const)
char& operator[](size_t i){assert(i < size());return _str[i];}const char& operator[](size_t i)const{assert(i < size());return _str[i];}
测试代码:
int main()
{yzc::string s1 = "159753468244";for (int i = 0; i < s1.size(); i++){cout << s1[i] << " ";}cout << endl;return 0;
}
运行结果:

二. 现代写法的string类模拟
2.1 拷贝构造函数
为了提高代码的复用,我们在拷贝构造函数中使用构造函数去构造一个对象,然后交换当前对象和这个对象。
代码如下:
//现代版写法string(const string& s):_str(nullptr){string strTmp(s._str); //使用构造函数将s._str构造一个tmpswap(_str, strTmp._str);//交换_str和tmp}
交换后,strTmp由于是局部变量就直接被销毁了! 而我们创建的对象被保留
2.2 赋值运算符重载
我们使用传值方法传参,交换_str后我们成功创建的对象。且不会影响传入的对象
//现代版写法string& operator=(string s) //不使用引用传参,而是传值{swap(_str, s._str);return *this;}
测试代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <assert.h>
using namespace std;namespace yzc
{class string{public:string(const char* str = ""):_str(new char[strlen(str) + 1]) //由于C语言字符串后面都带'\0',需要加上1{strcpy(_str, str);}拷贝构造,使用深拷贝完成拷贝//string(const string& s)// :_str(new char[s.size() + 1]) //注意要加1//{// strcpy(_str, s._str);//}//现代版写法string(const string& s):_str(nullptr){string strTmp(s._str); //使用构造函数将s._str构造一个tmpswap(_str, strTmp._str);//交换_str和tmp}赋值运算符重载//string& operator=(const string& s)//{// if (this != &s)//地址不同才进行赋值// {// delete[] _str; //释放原地址空间,防止内存泄漏// //定义中间变量tmp用于拷贝// char* tmp = new char[s.size() + 1];// strcpy(tmp, s._str);// _str = tmp;// // }// return *this; //返回当前对象,为了支持 a = b = c//}//现代版写法string& operator=(string s) //不使用引用传参,而是传值{swap(_str, s._str);return *this;}~string(){delete[] _str;_str = nullptr;}size_t size()const{return strlen(_str);}void print(){cout << _str << endl;}private:char* _str;};
}int main()
{yzc::string s1 = "123456";yzc::string s2(s1);yzc::string s3 = s1;s1.print();s2.print();s3.print();return 0;
}
运行结果:

三. 下篇文章:STL中vector的使用
相关文章:
16.C++STL 3(string类的模拟,深浅拷贝问题)
⭐本篇重点:string类的模拟,自己实现一个简单的string类 ⭐本篇代码:c学习/05.string类的学习 橘子真甜/c-learning-of-yzc - 码云 - 开源中国 (gitee.com) 目录 一. 经典string类的模拟 1.1 深浅拷贝问题 1.2 使用深拷贝完成经典string类的…...
神经网络10-Temporal Fusion Transformer (TFT)
Temporal Fusion Transformer (TFT) 是一种专为时序数据建模而设计的深度学习模型,它结合了Transformer架构和其他技术,旨在有效地处理和预测时序数据中的复杂模式。TFT 于 2020 年由 Google Research 提出,旨在解决传统模型在时序预测中的一…...
“iOS profile文件与私钥证书文件不匹配”总结打ipa包出现的问题
目录 文件和证书未加载或特殊字符问题 证书过期或Profile文件错误 确认开发者证书和私钥是否匹配 创建证书选择错误问题 申请苹果 AppId时勾选服务不全问题 总结 在上线ios平台的时候,在Hbuilder中打包遇见了问题,生成ipa文件时候,一…...
《图像梯度与常见算子全解析:原理、用法及效果展示》
简介:本文深入探讨图像梯度相关知识,详细介绍图像梯度是像素灰度值在不同方向的变化速度,并以 “pig.JPG” 图像为例,通过代码展示如何选取图像部分区域并分析其像素值以论证图像梯度与边缘信息的关联。接着全面阐述了 Sobel 算子,…...
【c++篇】:探索c++中的std::string类--掌握字符串处理的精髓
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨ ✨ 个人主页:余辉zmh–CSDN博客 ✨文章所属专栏:c篇–CSDN博客 文章目录 前言一.std::string对象的创建二.std::string对象的访问三.std::str…...
LlamaIndex ollama 搭建本地RAG应用,建立本地知识库
目录 简介安装前的准备下载ollama创建llamaindex conda环境,为后面编码作准备 环境变量迁移ollama到其他盘运行ollama方式一方式二禁止ollama开机自启动运行第一个模型 Chatbox聊天下载Chatbox配置ollama地址和模型验证 建立自身特定知识数据搭配大语言模型创建项目…...
draggable的el-dialog实现对话框标题可以选择
请看图 这个对话框使用了el-dialog并且draggable属性设置成了true,所以标题栏这里就可以拖动,现在用户想选中标题栏的文本进而复制。我看到这个需求头都大了。 我能想到的方案有三个:1. 取消draggable为true 2. 标题文案后面加一个复制按钮 …...
2024年Android面试总结
2024年Android面试总结 1.动画类型有哪些?插值器原理? 2.StringBuffer和StringBuilder区别? 3.jvm内存模型? 4.线程池7大核心参数及原理? 5.Android多进程通信方式有哪些?各自的优缺点? 6…...
树莓派3:64位系统串口(UART)使用问题的解决方法
前言 当我们要使用串口进行zigbee的短距离通信时,发现无法使用串口. 原因 树莓派3bCPU内部有两个串口,一个硬件串口(就是我们平时使用的UART),还有一个迷你串口(mini-uart),在老版本的树莓派中把硬件串口分配在GPIO上,可以单独使用.但是在新的树莓派中官方把硬件串口给了蓝牙…...
SemiDrive E3 硬件设计系列---唤醒电路设计
一、前言 E3 系列芯片是芯驰半导体高功能安全的车规级 MCU,对于 MCU 的硬件设计部分,本系列将会分模块进行讲解,旨在介绍 E3 系列芯片在硬件设计方面的注意事项与经验,本文主要讲解 E3 硬件设计中唤醒电路部分的设计。 二、RTC 模…...
淘宝接口高并发采集核心要点解读,开启电商数据智能应用新纪元
一、引言 在电商蓬勃发展的今天,淘宝作为全球知名的电商巨头,其平台上的数据犹如一座蕴藏无限价值的宝藏。准确且高效地采集淘宝接口数据,并通过高并发技术实现大规模数据获取,对于电商企业的精准营销、市场趋势分析、竞品监测以及…...
C#里怎么样快速使用LINQ实现查询?
C#里怎么样快速使用LINQ实现查询? 在C#里使用LINQ,是一个方便的功能, 不过,要学会使用这部分的功能,需要比较多的学习时间,否则,使用起就比较难。 因为它的表现方式,与编程语言通用的功能,还是差别比较大。 当数据量比较小,没有特定的顺序时,使用LINQ访问会比较好…...
2024新版微软edge浏览器输入百度网址时自动补全tn=68018901……小尾巴的解决
以前一直是Windows11 21h2版本,浏览器内输入baidu不会自动补全tnxx的百度推广小尾巴。然后前几天在BIOS内开启了tpm2.0,升级Windows11到了24h2版本。 发现在edge浏览器内只要输入b,就会自动补全为baidu.com?tnXXX的这么一个百度推广形式。开…...
uni-app打包H5自定义微信分享
1、配置分享信息 修改uni-app的index.html,添加Open Graph(OG)标签来配置分享信息。 <!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8" /><meta name="description" content="标题"/>…...
大模型专栏--大模型应用场景
紧接着第一篇,什么是大模型,这篇文章讨论一下大模型的应用场景和应用方式有哪些? 基础使用 随着 GPT 的出现,AI 大模型已经越来越多得出现在日常生活和学术研究,工作中。 按照使用方向有以下几种: 自然语…...
骑砍2霸主MOD开发(29)-顶点动画
一.定制化顶点动画(MorphAnimation) 定制化顶点动画用于人物Agent的面部表情. 1.创建MorphAnimation对应静态资源morph_animation.tpac 2.Agent设置对应MorphAnimation [EngineMethod("set_agent_facial_animation", false)] void SetAgentFacialAnimation(UIntPtr …...
-Dspring.profiles.active=dev与--spring.profiles.active=dev的区别
在Spring Boot应用程序中,-Dspring.profiles.activedev和--spring.profiles.activedev都用于指定要激活的Spring配置文件(profile),但它们在不同的环境中使用,并且有不同的作用域。 -Dspring.profiles.activedev&#…...
面向对象高级(2)单例设计对象与代码块
面向对象高级(2) 单例设计模式、main方法与代码块 引言; 设计模式:特定环境下特定问题的处理方法。可理解为一种经典的可以参照的模板。单例设计模式则是只存在单个对象实例、且只有一种方法获取对象实例的一种设计模式。 单例设…...
47小型项目的规划与实施
每天五分钟学Linux | 第四十七课:小型项目的规划与实施 大家好!欢迎再次来到我们的“每天五分钟学Linux”系列教程。在前面的课程中,我们学习了并发编程的知识,包括如何管理和使用进程与线程。今天,我们将探讨如何规划…...
堤防安全监测系统方案
一、背景情况 堤防是开发利用水资源和防治水灾害的重要工程措施之一,对防洪、供水、生态、发电、航运等至关重要。我国现有堤防9.8万多座,其中大中型堤防4700多座、小型堤防9.4万座,80%以上修建于上世纪50至70年代。由于堤防管护力量薄弱&am…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
