【C++】string类:模拟实现(适合新手的手撕string)

上次介绍了标准库里的string类以及常用接口:【C++】String类:标准库介绍-CSDN博客

本次就来亲自动手来模拟实现下


目录

一.基本结构

二.构造函数(constructor)

1.构造函数

2.拷贝构造

3.c_str()

三.析构函数(destructor)

四.operator=

五.迭代器(iterator)

六.容量操作(capacity)

七.修改操作(modify)

八.访问操作(access)

九.查找操作(find)

十.插入和删除(insert and erase)

十一.关系比较运算符(relational operators)

十二.流操作重载


一.基本结构

根据string标准库的文档:string - C++ Reference (cplusplus.com)

可以看见string类大致分为这么几个部分,那么我们的实现也根据这些功能来大致实现

此时,可以创建头文件:string.h  实现文件:string.cpp 测试文件:test.cpp 

在自定义的命名空间内创建string类,配上基本结构:

namespace zyh
{class string{public://各种函数private:char* _str;//没有设计成模板,直接使用char数组size_t _size;//有效字符的个数size_t _capacity;//分配空间的大小static const size_t npos;//在外初始化为-1};
}

二.构造函数(constructor)

1.构造函数

无参的构造函数,构造的是空字符串,但空字符串不代表什么都没有,只要是字符串,哪怕是空,也存在\0,这是字符串的标志,因此无参构造时也要开辟出一个char的空间并用\0来初始化

//无参
string():_str(new char[1]{'\0'}),_size(0) ,_capacity(0)
{}//有参
string(const char* str)
{_size = strlen(str);_capacity = _size;//capactiy不包含'\0'_str = new char[_capacity + 1];strcpy(_str, str);
}

当然也可以合二为一,直接使用缺省参数

string(const char* str = "")
{_size = strlen(str);_capacity = _size;//capactiy不包含'\0'_str = new char[_capacity + 1];strcpy(_str, str);
}

2.拷贝构造

传统写法

string(const string& s)
{_size = s._size;_capacity = s._capacity;_str = new char[_capacity + 1];strcpy(_str, s._str);
}

现代写法:使用中转站临时对象tmp来交换得到

要使用s1来拷贝构造s3,那么只需要使用s1的字符串来构造一个临时对象tmp,再将s3的内容与tmp进行交换即可

void swap(String& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}string(const string& s) 
{string tmp(s._str);swap(tmp);
}

3.c_str()

目前还没有对string类进行流操作的重载,因此想要打印字符串,此处提供c_str()方法来得到成员_str,以此来进行打印

const char* c_str()const
{return _str;
}

三.析构函数(destructor)

~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}

四.operator=

注意=(赋值运算)是有返回值的,返回值就是它本身,因此返回值类型为string&,同时为了防止自身给自身赋值,加入判断this是否指向参数,否则会出错

string& operator=(const string& s)
{if (this != &s){//清除旧空间delete[] _str;_size = s._size;_capacity = s._capacity;//开辟新空间_str = new char[_capacity + 1];//并且复制strcpy(_str, s._str);}return *this;
}

五.迭代器(iterator)

这里我们使用指针来模拟迭代器,那么就将char*类型的指针重命名为iterator

同理,那么const_iterator是针对常量的,那么就是对应const char*

//迭代器
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{return &_str[0];
}
iterator end()
{return &_str[_size];
}
const_iterator begin()const
{return _str;
}
const_iterator end()const
{return _str + _size;
}

六.容量操作(capacity)

//capacity
size_t size()const
{return _size;
}
size_t capacity()const
{return _capacity;
}
bool empty()const
{return (_size == 0);
}
void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];_capacity = n;strcpy(tmp, _str);delete[] _str;_str = tmp;}
}
void resize(size_t n, char c)
{if (n > _size){int i = n - _size;while (i--){push_back(c);}}_size = n;
}

七.修改操作(modify)

void push_back(char c)
{//扩容if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);//考虑空的情况}_str[_size++] = c;_str[_size] = '\0';
}
string& operator+=(char c)
{push_back(c);return *this;
}
void append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size = _size + len;
}
string& operator+=(const char* str)
{append(str);return *this;
}
void clear()
{char* tmp = new char[_capacity + 1];tmp[0] = '\0';_str = tmp;_size = 0;
}

八.访问操作(access)

//access
char& operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}
const char& operator[](size_t pos)const
{assert(pos >= 0 && pos < _size);return _str[pos];
}

九.查找操作(find)

size_t find(char c, size_t pos) const
{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == c){return i;}return npos;}
}
size_t find(const char* s, size_t pos) const
{assert(pos < _size);const char* ptr = strstr(_str + pos, s);if (ptr == nullptr){return npos;}else{return ptr - _str;}
}

十.插入和删除(insert and erase)

//在pos位置上插入字符c
string& insert(size_t pos, char c)
{assert(pos <= _size);if (_size == _capacity){reserve((_capacity == 0) ? 4 : 2 * _capacity);}//向后移动size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = c;_size++;return *this;
}//在pos位置上插入字符串str
string& insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (end > pos + len - 1){_str[end] = _str[end - len];end--;}for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;return *this;
}string& erase(size_t pos, size_t len)
{assert(pos < _size);if (len >= _size - pos){_str[pos] = '\0';_size = pos;}else{for (size_t i = pos + len; i <= _size; i++){_str[i - len] = _str[i];}_size -= len;}return *this;
}

十一.关系比较运算符(relational operators)

//使用strcmp实现两个即可,此处实现了小于和等于
//其余的使用已实现的逻辑即可
bool operator<(const string& s)
{return strcmp(_str, s._str) < 0;
}
bool operator<=(const string& s)
{return (*this < s) || (*this == s);
}
bool operator>(const string& s)
{return !(*this <= s);
}
bool operator>=(const string& s)
{return !(*this < s);
}
bool operator==(const string& s)
{return strcmp(_str, s._str) == 0;
}
bool operator!=(const string& s)
{return !(*this == s);
}

十二.流操作重载

注意必须是全局声明,为什么?因为如果是成员函数,那么只需用this指针即可,但这样的话,就必须要string<<cout ( string.operator<<(cout) )了,很明显与平时使用的顺序不同,为了调整参数位置使得其按正确顺序,就必须声明在全局而不使用this指针,这一点友元就能做到。

还有值得注意的一点是返回值,必须是ostream&,这是为了能够连续的使用流操作符

//在外部实现,内部声明友元类
ostream& operator<<(ostream& _cout, const zyh::string& s)
{for (auto ch : s){_cout << ch;}return _cout;
}
istream& operator>>(istream& _cin, zyh::string& s)
{s.clear();//防止频繁扩容const int N = 256;char buff[N];int i = 0;char ch = _cin.get();while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}//s += ch;ch = _cin.get();}buff[i] = '\0';s += buff;return _cin;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/406266.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux PXE批量网络装机及Kickstart无人值守安装

目录 一、PXE基本概述 1.1 什么是PXE 1.2 PXE批量部署的优点 1.3 PXE部署的前置条件 二、部署PXE远程安装服务器 2.1 安装并启动TFTP服务 2.2 安装并启动DHCP服务 2.3 准备linux内核、初始化镜像文件 2.4 准备PXE引导程序 2.5 安装FTP服务&#xff0c;准备CentOS 7 安…

Solidworks 创建工程图纸,工程图纸不显示解决

当完成三维零件&#xff0c;制作工程图纸时&#xff0c;发现右侧“工程图图纸”不显示了&#xff0c;不能像以前那样方便的拖拽了。如下图&#xff1a; 解决办法&#xff1a; 步骤1:点击这 ...&#xff0c;打开需要的三维图文件&#xff0c;如“公头主体” 步骤2&#xff1a;…

VisualStudio|开发环境相关技巧及问题

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 本节继续学习VisualStudio相关内容&#xff0c;以前学习都是以能用为主&#xff0c;没有系统的学习&#xff0c;接下来会系统的学习相关内容&#xff0c; 以下为学习笔记。 01 第三方dll调用 ①&#xff1a;如果第三…

希尔排序

希尔排序是直接排序的优化版本。 希尔排序是将庞大的数据进行分组&#xff0c;通过定义一个gap值&#xff0c;将数组里面间隔为这个gap值的元素分在一个小组里面&#xff0c;把每个小组通过插入排序的方式分别排成有序 在一组组排成有序的这个过程中&#xff0c;原来无序的数…

快速上手体验MyPerf4J监控springboot应用(docker版快速开始-本地版)

使用MyPerf4J监控springboot应用 快速启动influxdb时序数据库日志收集器telegrafgrafana可视化界面安装最终效果 项目地址 项目简介: 一个针对高并发、低延迟应用设计的高性能 Java 性能监控和统计工具。 价值 快速定位性能瓶颈快速定位故障原因 快速启动 监控本地应用 idea配…

M8020A J-BERT 高性能比特误码率测试仪

M8020A 比特误码率测试仪 J-BERT M8020A 高性能 BERT 产品综述 Keysight J-BERT M8020A 高性能比特误码率测试仪能够快速、准确地表征传输速率高达 16 或 32 Gb/s 的单通道和多通道器件中的接收机。 M8020A 综合了更广泛的功能&#xff0c;可以简化您的测试系统。 自动对信…

qiankun微前端

qiankun微前端 主项目1、安装qiankun2、main.js引入注册 二、子项目1、安装sh-winter/vite-plugin-qiankun2、main.js配置3、vite.config.js配置 三、问题解决四、一键启动 主项目 1、安装qiankun npm i qiankun -S2、main.js引入注册 import { createApp } from vue import…

健身房管理系统--论文pf

TOC springboot542健身房管理系统--论文pf 第1章 绪论 1.1选题动因 当前的网络技术&#xff0c;软件技术等都具备成熟的理论基础&#xff0c;市场上也出现各种技术开发的软件&#xff0c;这些软件都被用于各个领域&#xff0c;包括生活和工作的领域。随着电脑和笔记本的广泛…

C语言之字节对齐

目录 1. 引言2.字节对齐原理3.字节对齐应用4.总结 1. 引言 字节对齐属于编译器的内容&#xff0c;决定数据实际的存放方式。主要有两个作用&#xff1a;1.优化数据储存&#xff0c;减少空间浪费 2.增加数据读取速率&#xff0c;本文将于以上两点展开&#xff0c;简述字节对齐的…

VLM调研记录

Visual Autoregressive Modeling: Scalable Image Generation via Next-Scale Prediction 北大和字节团队的一篇VLM&#xff0c;在生成任务上&#xff0c;用GPT范式&#xff0c;声称在FID上超过了DIT&#xff0c;SD3和SORA。开源。首先是multi-scale的VQVAE&#xff0c;然后是…

一起学习LeetCode热题100道(52/100)

52.腐烂的橘子(学习) 在给定的 m x n 网格 grid 中&#xff0c;每个单元格可以有以下三个值之一&#xff1a; 值 0 代表空单元格&#xff1b; 值 1 代表新鲜橘子&#xff1b; 值 2 代表腐烂的橘子。 每分钟&#xff0c;腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。 返…

PHP轻创推客集淘客地推任务平台于一体的综合营销平台系统源码

&#x1f680;轻创推客&#xff0c;营销新纪元 —— 集淘客与地推任务于一体的全能平台&#x1f310; &#x1f308;【开篇&#xff1a;营销新潮流&#xff0c;轻创推客引领未来】 在瞬息万变的营销世界里&#xff0c;你还在为寻找高效、全面的营销渠道而烦恼吗&#xff1f;&…

【数据安全】数据中心数据安全整体解决方案(Doc完整版)

第一章 解决方案 1.1 建设需求 1.2 建设思路 1.3 总体方案 信息安全系统整体部署架构图 1.3.1 IP准入控制系统 1.3.2 防泄密技术的选择 1.3.3 主机账号生命周期管理系统 1.3.4 数据库账号生命周期管理系统 1.3.5 双因素认证系统 1.3.6 数据库审计系统 1.3.7 数据脱敏…

图数据库查询语言 Cypher 基础

Cypher 是 Neo4j 的声明式查询语言&#xff0c;为属性图提供了富有表现力和高效的查询&#xff0c;是一种成熟和直观的图数据库查询语言。在图上执行任何类型的创建、读取、更新或删除(CRUD)&#xff0c;Cypher 是 Neo4j 的主要接口。 本文介绍了 Cypher 基础知识&#xff0c;…

软件测试用例的编写(六)

软件测试用例 定义 测试用例&#xff08;TestCase&#xff09;是为项目需求而编制的一组测试输入&#xff0c;执行步骤&#xff0c;以及预期结果&#xff0c;以便测试某个程序是否满足客户需求 可以总结为&#xff1a;每一个测试点的数据设计和步骤设计 – 对测试点的细化 作…

大数据技术之Zookeeper安装 (2)

目录 下载地址 本地模式安装 1&#xff09;安装前准备 2&#xff09;配置修改 3&#xff09;操作 Zookeeper 配置参数解读 Zookeeper 集群操作 集群规划 解压安装 配置服务器编号 配置 zoo.cfg 文件 集群操作 Zookeeper 集群启动停止脚本 创建脚本 增加脚本执行权限 …

在线问诊平台开发指南:基于互联网医院系统源码的实现路径

今天&#xff0c;小编将详细讲解如何通过互联网医院系统源码开发在线问诊平台。 一、在线问诊平台的需求分析 在线问诊平的核心目标是通过互联网技术&#xff0c;实现患者与医生之间的远程交流与诊断。因此&#xff0c;在开发过程中&#xff0c;首先需要明确平台的核心功能需求…

将 hugo 博客搬迁到服务器

1. 说明 在 Ubuntu 22.04 上使用 root 账号&#xff0c;创建普通账号&#xff0c;并赋予 root 权限。 演示站点&#xff1a;https://woniu336.github.io/ 魔改hugo主题: https://github.com/woniu336/hugo-magic 2. 服务器配置 建立 git 用户 adduser git安装 git sudo apt …

SpringBoot笔记01

第1章 Spring Boot概要 1.1 SpringBoot介绍 随着动态语言的流行&#xff08;Ruby、Scala、Node.js&#xff09;, Java的开发显得格外的笨重&#xff1b;繁多的配置、低下的开 发效率、复杂的部署流程以及第三方技术整合难度大。 在上述环境下&#xff0c;Spring Boot由此诞生…

光伏检测气象站:实时监测:高效管理

随着全球对可再生能源需求的日益增长&#xff0c;光伏发电作为清洁能源的重要组成部分&#xff0c;其重要性日益凸显。然而&#xff0c;光伏发电的效率与稳定性受气象条件影响显著&#xff0c;如光照强度、温度、湿度、风速等因素均能直接影响光伏板的发电效率。因此&#xff0…