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

C++:string容器(下篇)

1.string浅拷贝的问题

// 为了和标准库区分,此处使用String
class String
{
public :/*String():_str(new char[1]){*_str = '\0';}*///String(const char* str = "\0") // 错误示范//String(const char* str = nullptr) // 错误示范String(const char* str = ""){if (nullptr == str){assert(false);return;} _str = new char[strlen(str) + 1];strcpy(_str, str);   }~String(){if (_str){delete[] _str;_str = nullptr;}}
private:char* _str;
};int main()
{String s1("hello world!");String s2(s1);    // 这里会析构两次,导致程序崩溃return 0;
}

说明:

        上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝

浅拷贝:

        也称位拷贝编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规

        可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。

2.深拷贝

        如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

2.1 传统写法的String类

class String
{
public :String(const char* str = ""){if (nullptr == str){assert(false);return;} _str = new char[strlen(str) + 1];strcpy(_str, str);} String(const String& s): _str(new char[strlen(s._str) + 1]){strcpy(_str, s._str);} String& operator=(const String& s){if (this != &s){char* pStr = new char[strlen(s._str) + 1];strcpy(pStr, s._str);delete[] _str;_str = pStr;} return* this;} ~String(){if (_str){delete[] _str;_str = nullptr;}}private:char* _str;
};

2.1 现代写法的String类

class String
{
public :String(const char* str = ""){// 构造String类对象时,如果传递nullptr指针,可以认为程序非if (nullptr == str){assert(false);return;} _str = new char[strlen(str) + 1];strcpy(_str, str);} String(const String& s): _str(new char[strlen(s._str) + 1]){strcpy(_str, s._str);} // 现代版本String & operator=(String s){std::swap(_str, s._str);return *this;}传统版本//String& operator=(const String& s)//{//	if (this != &s)//	{//		char* pStr = new char[strlen(s._str) + 1];//		strcpy(pStr, s._str);//		delete[] _str;//		_str = pStr;//	} //	return* this;//} ~String(){if (_str){delete[] _str;_str = nullptr;}}private:char* _str;
};

3.string类的模拟实现

头文件 string.h:

namespace room
{class string{public:// 迭代器typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}/*string():_str(new char[1]{'\0'}),_size(0),_capacity(0){}*/void swap(string& s);string(size_t n, char ch);string(const char* str = "");// s2(s1)string(const string& s);// s1 = s2// s1 = s1//string& operator=(const string& s);// s1 = s2string& operator=(string s);~string();void clear(){_str[0] = '\0';_size = 0;}const char* c_str() const{return _str;}void reserve(size_t n);void push_back(char ch);void append(const char* str);string& operator+=(char ch);string& operator+=(const char* str);void insert(size_t pos, size_t n, char ch);void insert(size_t pos, const char* ch);void erase(size_t pos = 0, size_t len = npos);size_t find(char ch, size_t pos = 0);size_t find(const char* str, size_t pos = 0);size_t size() const{return _size;}size_t capacity() const{return _size;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}const char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}string substr(size_t pos, size_t len = npos);bool operator==(const string& s) const;bool operator!=(const string& s) const;bool operator<(const string& s) const;bool operator<=(const string& s) const;bool operator>(const string& s) const;bool operator>=(const string& s) const;private:// 声明char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;const static size_t npos;};ostream& operator<<(ostream& out, const string& s);istream& operator>>(istream& in, string& s);istream& getline(istream& in, string& s, char delim);
}

源文件string.cpp:

// 链接时会合并
namespace room 
{const size_t string::npos = -1;string::string(size_t n, char ch):_str(new char[n + 1]),_size(n),_capacity(n){for (size_t i = 0; i < n; ++i){_str[i] = ch;}_str[_size] = '\0';}string::string(const char* str):_size(strlen(str)){_capacity = _size;_str = new char[_size + 1];strcpy(_str, str);}传统写法s2(s1)//string::string(const string& s)//{//	_str = new char[s._capacity + 1];//	strcpy(_str, s._str);//	_size = s._size;//	_capacity = s._capacity;//}void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// 现代写法// s2(s1)string::string(const string& s){string tmp(s._str);swap(tmp);}// s1 = s2// s1 = s1/*string& string::operator=(const string& s){if (this != &s){delete[] _str;_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}return *this;}*/// s1 = s2string& string::operator=(string s){swap(s);return *this;}string::~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void string::push_back(char ch){if (_size + 1 > _capacity){// 扩容reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;++_size;_str[_size] = '\0';	// 末尾得加上一个\0}void string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){// 扩容size_t newCapacity = 2 * _capacity;if(_size + len > 2 * _capacity){newCapacity = _size + len;}reserve(newCapacity);}strcpy(_str + _size, str);_size += len;}string& string::operator+=(char ch){push_back(ch);return *this;}string& string::operator+=(const char* str){append(str);return *this;}void string::insert(size_t pos, size_t n, char ch){assert(pos <= _size);assert(n > 0);if (_size + n > _capacity){// 扩容size_t newCapacity = 2 * _capacity;if (_size + n > 2 * _capacity){newCapacity = _size + n;}reserve(newCapacity);}// 挪动数据// 这样挪动数据,头插的时候会越界/*size_t end = _size;while (end >= pos){_str[end + n] = _str[end];--end;}*/size_t end = _size + n;while (end > pos + n - 1){_str[end] = _str[end - n];--end;}for (size_t i = 0; i < n; ++i){_str[pos + i] = ch;}_size += n;/*string tmp(n, ch);insert(pos, tmp.c_str());*/}void string::insert(size_t pos, const char* str){assert(pos <= _size);size_t n = strlen(str);if (_size + n > _capacity){// 扩容size_t newCapacity = 2 * _capacity;if (_size + n > 2 * _capacity){newCapacity = _size + n;}reserve(newCapacity);}size_t end = _size + n;while (end > pos + n - 1){_str[end] = _str[end - n];--end;}for (size_t i = 0; i < n; ++i){_str[pos + i] = str[i];}}void string::erase(size_t pos, size_t len){if (len >= _size - pos){// 删完数据_str[pos] = '\0';_size = pos;}else{size_t end = pos + len;while (end <= _size){_str[end - len] = _str[end];++end;}_size -= len;}}size_t string::find(char ch, size_t pos){for (size_t i = pos; i < _size; ++i){if (_str[i] == ch){return i;}}return npos;}size_t string::find(const char* str, size_t pos){const char* p = strstr(_str + pos, str);if (p == nullptr){return npos;}else{return p - _str;}}string string::substr(size_t pos, size_t len){size_t leftlen = _size - pos;// 给的长度大于剩余的长度时,len等于剩余长度if (len > leftlen)len = leftlen;string tmp;tmp.reserve(len);for (size_t i = 0; i < len; ++i){tmp += _str[pos + i];}return tmp;}bool string::operator==(const string& s) const{return strcmp(_str, s._str) == 0;}bool string::operator!=(const string& s) const{return !(*this == s);}bool string::operator<(const string& s) const{return strcmp(_str, s._str) < 0;}bool string::operator<=(const string& s) const{return *this < s || *this == s;}bool string::operator>(const string& s) const{return !(*this <= s);}bool string::operator>=(const string& s) const{return !(*this < s);}ostream& operator<<(ostream& out, const string& s){for (auto ch : s)out << ch;return out;}istream& operator>>(istream& in, string& s){s.clear();// 输入短串,不会浪费空间// 输入长串,避免不断扩容const size_t N = 1024;char buff[N];int i = 0;//cin >> i;	// 这样是不行的char ch = in.get();	// 用get()才能收到空格和换行// 短串就放入buffwhile (ch != ' ' && ch != '\n')	// 遇到\n就结束{buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){// 长串才扩容,减小扩容带来的性能消耗buff[i] = '\0';s += buff;}return in;}istream& getline(istream& in, string& s, char delim){s.clear();// 输入短串,不会浪费空间// 输入长串,避免不断扩容const size_t N = 1024;char buff[N];int i = 0;//cin >> i;	// 这样是不行的char ch = in.get();	// 用get()才能收到空格和换行// 短串就放入buffwhile (ch != delim)	// 遇到指定字符delim就结束{buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){// 长串才扩容,减小扩容带来的性能消耗buff[i] = '\0';s += buff;}return in;}
}

相关文章:

C++:string容器(下篇)

1.string浅拷贝的问题 // 为了和标准库区分&#xff0c;此处使用String class String { public :/*String():_str(new char[1]){*_str \0;}*///String(const char* str "\0") // 错误示范//String(const char* str nullptr) // 错误示范String(const char* str …...

2.数据结构-栈和队列

数据结构-栈和队列 2.1栈2.1.1栈的表示和实现2.1.2栈的应用举例数制转换括号匹配检验迷宫给求解表达式求值 2.1.3链栈的表示和实现2.1.4栈与递归的实现遍历输出链表中各个结点的递归算法*Hanoi塔问题的递归算法 2.2队列2.2.1循环队列——队列的顺序表示和实现2.2.2链队——队列…...

aws(学习笔记第三十一课) aws cdk深入学习(batch-arm64-instance-type)

aws(学习笔记第三十一课) aws cdk深入学习 学习内容&#xff1a; 深入练习aws cdk下部署batch-arm64-instance-type 1. 深入练习aws cdk下部署batch-arm64-instance-type 代码链接 代码链接 代码链接 -> batch-arm64-instance-type之前代码学习 之前学习代码链接 -> aw…...

MySQL 中,SELECT ... FOR UPDATE

在 MySQL 中&#xff0c;SELECT ... FOR UPDATE 语句会对查询结果集中的行加排他锁&#xff08;X 锁&#xff09;。关于其他事务是否能读取当前行&#xff0c;以下是详细说明&#xff1a; 1. 排他锁&#xff08;X 锁&#xff09;的特性 排他锁是一种独占锁&#xff0c;加锁后&…...

云服务运维智能时代:阿里云操作系统控制台

阿里云操作系统控制台 引言需求介绍操作系统使用实例获得的帮助与提升建议 引言 阿里云操作系统控制台是一款创新型云服务器运维工具&#xff0c;专为简化用户的运维工作而设计。它采用智能化和可视化的方式&#xff0c;让运维变得更加高效、直观。借助AI技术&#xff0c;控制…...

【Agent的革命之路——LangGraph】如何使用config

有时我们希望在调用代理时能够对其进行配置。这包括配置使用哪个语言模型&#xff08;LLM&#xff09;等例子。下面我们将通过一个示例来详细介绍如何进行这样的配置。 在介绍 configurable 之前我们先介绍一下 Langchain 的 RunnableConfig。RunnableConfig是一个配置对象&…...

ArcGIS操作:15 计算点的经纬度,并添加到属性表

注意&#xff1a;需要转化为地理坐标系 1、打开属性表&#xff0c;添加字段 2、计算字段&#xff08;以计算纬度为例 !Shape!.centroid.Y ) 3、效果...

Docker基础入门

第 1 章&#xff1a;核心概念与安装配置 本章首先介绍Docker 的三大核心概念&#xff1a; 镜像 (Image)容器&#xff08;Container)仓库&#xff08;Repository&#xff09; 只有理解了这三个核心概念&#xff0c;才能顺利地理解Docker容器的整个生命周期。 随后&#xff0…...

【Linux】详谈 基础I/O

目录 一、理解文件 狭义的理解&#xff1a; 广义理解&#xff1a; 文件操作的归类认知 系统角度 二、系统文件I/O 2.1 标志位的传递 系统级接口open ​编辑 open返回值 写入文件 读文件 三、文件描述符 3.1&#xff08;0 & 1 & 2&#xff09; 3.2 文件描…...

爬虫案例七Python协程爬取视频

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Python协程爬取视频 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 爬虫案例七协程爬取视频 提示&#xff1a;以下是本篇文章正文…...

[20250304] 关于 RISC-V芯片 的介绍

[20250304] 关于 RISC-V芯片 的介绍 1. 调研报告 一、RISC-V 芯片结构分析 RISC-V 芯片基于开源指令集架构&#xff08;ISA&#xff09;&#xff0c;其核心优势在于模块化设计与高度灵活性。 指令集架构 基础指令集&#xff1a;包含 RV32I&#xff08;32 位&#xff09;、R…...

一学就会:A*算法详细介绍(Python)

&#x1f4e2;本篇文章是博主人工智能学习以及算法研究时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对相关等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在&am…...

Hadoop、Hive、Spark的关系

Part1&#xff1a;Hadoop、Hive、Spark关系概览 1、MapReduce on Hadoop 和spark都是数据计算框架&#xff0c;一般认为spark的速度比MR快2-3倍。 2、mapreduce是数据计算的过程&#xff0c;map将一个任务分成多个小任务&#xff0c;reduce的部分将结果汇总之后返回。 3、HIv…...

Excel·VBA江西省预算一体化工资表一键处理

每月制作工资表导出为Excel后都需要调整格式&#xff0c;删除0数据的列、对工资表项目进行排序、打印设置等等&#xff0c;有些单位还分有“行政”、“事业”2个工资表就需要操作2次。显然&#xff0c;这种重复操作的问题&#xff0c;可以使用VBA代码解决 目录 代码使用说明1&a…...

23种设计模式简介

一、创建型&#xff08;5种&#xff09; 1.工厂方法 总店定义制作流程&#xff0c;分店各自实现特色披萨&#xff08;北京店-烤鸭披萨&#xff0c;上海店-蟹粉披萨&#xff09; 2.抽象工厂 套餐工厂&#xff08;家庭装含大披萨薯条&#xff0c;情侣装含双拼披萨红酒&#…...

python fire 库与 sys.argv 处理命令行参数

fire库 Python Fire 由Google开发&#xff0c;它使得命令行接口&#xff08;CLI&#xff09;的创建变得容易。使用Python Fire&#xff0c;可以将Python对象&#xff08;如类、函数或字典&#xff09;转换为可以从终端运行的命令行工具。这能够以一种简单而直观的方式与你的Py…...

PDF处理控件Aspose.PDF,如何实现企业级PDF处理

PDF处理为何成为开发者的“隐形雷区”&#xff1f; “手动调整200页PDF目录耗时3天&#xff0c;扫描件文字识别错误导致数据混乱&#xff0c;跨平台渲染格式崩坏引发客户投诉……” 作为开发者&#xff0c;你是否也在为PDF处理的复杂细节消耗大量精力&#xff1f;Aspose.PDF凭…...

Spring(1)——mvc概念,部分常用注解

1、什么是Spring Web MVC&#xff1f; Spring MVC 是一种基于 Java 的实现了 MVC&#xff08;Model-View-Controller&#xff0c;模型 - 视图 - 控制器&#xff09;设计模式的 Web 应用框架&#xff0c;它是 Spring 框架的一个重要组成部分&#xff0c;用于构建 Web 应用程序。…...

C语言(23)

字符串函数 11.strstr函数 1.1函数介绍&#xff1a; 头文件&#xff1a;string.h char *strstr ( const char * str1,const char *str2); 作用&#xff1a;在一个字符串&#xff08;str1&#xff09;中寻找另外一个字符串&#xff08;str2&#xff09;是否出现过 如果找到…...

Immich自托管服务的本地化部署与随时随地安全便捷在线访问数据

文章目录 前言1.关于Immich2.安装Docker3.本地部署Immich4.Immich体验5.安装cpolar内网穿透6.创建远程链接公网地址7.使用固定公网地址远程访问 前言 小伙伴们&#xff0c;你们好呀&#xff01;今天要给大家揭秘一个超炫的技能——如何把自家电脑变成私人云相册&#xff0c;并…...

【CP AUTOSAR】Icu驱动模块:从原理到实战的配置与优化指南

1. Icu驱动模块在AUTOSAR架构中的核心作用 第一次接触AUTOSAR的Icu模块时&#xff0c;我完全被它复杂的配置项搞懵了。直到在S32K3项目上实际调试电机转速测量功能&#xff0c;才真正理解这个模块的价值。简单来说&#xff0c;Icu就像汽车电子系统的"脉搏检测仪"&…...

GPT-5 API 费率全拆解:2026 各平台真实价格对比,附省钱方案

GPT-5 API 费率全拆解&#xff1a;2026 各平台真实价格对比&#xff0c;附省钱方案 前言 2026 年大模型 API 竞争进入白热化阶段&#xff0c;GPT-5 各平台价格差异巨大。本文实测对比主流平台费率&#xff0c;帮你找到最佳方案。 各平台费率对比 平台GPT-5 输入GPT-5 输出特…...

深入解析IoU(Jaccard系数)在目标检测中的关键作用与高效实现

1. IoU究竟是什么&#xff1f;从基础概念到视觉理解 第一次接触目标检测时&#xff0c;我对着论文里满屏的"IoU"缩写发懵——这到底是个什么魔法指标&#xff1f;后来在调试YOLO模型时才发现&#xff0c;这个看似简单的比值&#xff0c;实际上是整个检测任务的基石性…...

从零到一:在Windows系统上部署JDK11与Neo4j 4.3.5开发环境

1. 环境准备&#xff1a;JDK11与Neo4j 4.3.5的版本选择 刚开始接触Java和图数据库时&#xff0c;我踩过不少版本不兼容的坑。比如有一次装了最新版JDK17&#xff0c;结果Neo4j死活启动不了&#xff0c;折腾半天才发现是版本冲突。所以现在每次搭建环境&#xff0c;我都会先确认…...

自定义调色盘组件

示例效果&#xff1a;调色盘组件代码&#xff1a;使用input[typecolor]实现<template><div class"color-plate-page"><div class"color-div" click.stop"onColorDivClick"></div><div class"color-plate" …...

并发编程进阶:volatile、内存屏障与 CPU 缓存机制详解

知识点回顾 1. 什么是CQRS&#xff1f; CQRS是Command Query Responsibility Segregation的缩写&#xff0c;一般称作命令查询职责分离。从字面意思理解&#xff0c;就是将命令&#xff08;写入&#xff09;和查询&#xff08;读取&#xff09;的责任划分到不同的模型中。 对比…...

RePKG:解锁Wallpaper Engine资源宝库的终极提取与转换工具

RePKG&#xff1a;解锁Wallpaper Engine资源宝库的终极提取与转换工具 【免费下载链接】repkg Wallpaper engine PKG extractor/TEX to image converter 项目地址: https://gitcode.com/gh_mirrors/re/repkg RePKG 是一款专为Wallpaper Engine设计的开源C#工具&#xff…...

告别WechatRealFriends:WeFriends帮你实现微信好友关系管理无缝过渡

告别WechatRealFriends&#xff1a;WeFriends帮你实现微信好友关系管理无缝过渡 【免费下载链接】WechatRealFriends 微信好友关系一键检测&#xff0c;基于微信ipad协议&#xff0c;看看有没有朋友偷偷删掉或者拉黑你 项目地址: https://gitcode.com/gh_mirrors/we/WechatRe…...

AI工具使用限制解决方案:突破设备识别与权限重置完全指南

AI工具使用限制解决方案&#xff1a;突破设备识别与权限重置完全指南 【免费下载链接】go-cursor-help 解决Cursor在免费订阅期间出现以下提示的问题: Youve reached your trial request limit. / Too many free trial accounts used on this machine. Please upgrade to pro. …...

告别绿幕!安卓免Root虚拟视频插件开发避坑指南:从Media3播放到Xposed Hook的完整流程

安卓虚拟视频插件开发实战&#xff1a;从Media3解码到系统Hook的避坑指南 在移动端开发领域&#xff0c;音视频处理与系统级功能结合一直是技术难点与创新点交汇处。许多开发者尝试过在安卓平台上实现摄像头替换功能&#xff0c;却往往在视频编解码、系统API拦截和性能优化等环…...