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

C++之模拟实现string

文章目录

  • 前言
  • 一、包含的相关头文件
  • 二、构造和析构
    • 1.构造函数
    • 2.拷贝构造
      • 1.传统写法
      • 2.现代写法
    • 3.赋值运算符重载
      • 1.传统写法
      • 2.现代写法
    • 4.析构函数
  • 三、iterator
  • 四、modify
    • 1.push_back(尾插一个字符)
    • 2.append(尾插一个字符串)
    • 3.运算符重载+=
      • 1.尾插字符
      • 2.尾插字符串
    • 4.clear
    • 5.insert
      • 1.插入一个字符
      • 2.插入一个字符串
    • 6.erase
  • 五、capacity
    • 1.size
    • 2.capacity
    • 3.empty
    • 4.resize
    • 5.reserve
  • 六、access
    • 1.普通对象的接口(可读可写)
    • 2.const对象的接口(只读)
  • 七、relational operators
    • 1.运算符<重载
    • 2.运算符>重载
    • 3.运算符<=重载
    • 4.运算符>=重载
    • 5.运算符==重载
    • 6.运算符!=重载
  • 八、String operations
    • 1.find
      • 1.找字符
      • 2.找子串
    • 2.c_str
  • 九、Non-member function overloads
    • 1.流插入
    • 2.流提取
  • 十、私有属性
  • 总结


前言

因为学习了string的相关知识,了解了string大部分接口的底层实现原理,所以我决定自己模拟实现一个mini版的string类,用来加深对string各方面知识的理解。
如果有错误或不足之处,还望各位读者小伙伴们指出。


一、包含的相关头文件

#include<iostream>
#include<assert.h>
#include<cstring>
using std::ostream;
using std::istream;
using std:: cout;
using std:: endl;

二、构造和析构

1.构造函数

		//构造string(const char* str = ""){size_t len = strlen(str);_capacity = _size = len;_str = new char[_capacity + 1];strcpy(_str, str);}

2.拷贝构造

1.传统写法

该对象自己一点一点的进行深拷贝

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

2.现代写法

找一个中间对象,让这个中间对象用参数的值进行直接构造,再将这个中间对象的内容与自己的内容进行交换。相较于传统写法,现代写法更加简洁。

		//拷贝构造string(const string& s):_str(nullptr),//此处要注意将该对象的地址赋值为nullptr,否则析构中间对象时会因为发生野指针的访问而导致程序崩溃。_size(0),_capacity(0){string temp(s);swap(temp);}

此处的swap用的是string自己实现的swap,为什么不用库里的swap呢?因为库里的swap是进行深拷贝,会降低效率。我们自己实现的swap只需要进行浅拷贝,即将它们对应的资源进行交换即可。

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

3.赋值运算符重载

1.传统写法

		//赋值运算符重载string& operator=(const string &s){if (this != &s){char* temp = new char[s._capacity + 1];//用一个中间值记录新开辟的空间,如果开辟空间成功,再将该空间的地址给_str。避免因为开辟空间失败,_str原本的内容也销毁的情况发生。strcpy(temp, s._str);delete[] _str;_str = temp;_capacity = s._capacity;_size = s._size;}return *this;}

2.现代写法

与拷贝构造的现代写法思想类似,可以使程序更加简洁。并且此处不需要专门定义一个中间对象,因为传值传参传过来的形参是实参拷贝构造出来的对象,它就是一个很好的中间对象,我们直接和它进行交换即可。

		//赋值运算符重载string& operator=(string s){swap(s);return *this;}

4.析构函数

		//析构~string(){delete[] _str;_str = nullptr;//释放指针所指向的空间后要将该指针置为空(避免出现野指针的非法访问)_size = _capacity = 0;}

三、iterator

迭代器是一个使用起来像指针的东西,实际上string的迭代器就是char*类型的指针,因此我们先在最开始进行typedef

typedef char* iterator;

定义两个迭代器:分别指向字符串开头和结尾

		iterator begin(){return _str;}iterator end(){return _str + _size;}

四、modify

1.push_back(尾插一个字符)

string只提供了push_back的接口没有提供头插的接口,因为头插需要移动数据,效率很低,所以尽量不要在string中使用头插。

		void push_back(char c){if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;//要考虑到如果初始字符串的容量为0的情况reserve(newcapacity);_capacity = newcapacity;}_str[_size++] = c;_str[_size] = '\0';}

2.append(尾插一个字符串)

		void append(const char* str){size_t len = strlen(str);if (len + _size > _capacity){reserve(len + _size);}strcpy(end(), str);_size += len;}

3.运算符重载+=

1.尾插字符

尾插一个字符(复用尾插)

		string& operator+=(char c){push_back(c);return *this;}

2.尾插字符串

尾插一个字符串(复用append)

		string& operator+=(const char* str){append(str);return *this;}

4.clear

清除string中所有元素

		void clear(){_size = 0;_str[0] = '\0';}

5.insert

1.插入一个字符

在字符串某位置插入一个字符:

		// 在pos位置上插入字符c/字符串strstring& insert(size_t pos, char c){assert(pos <= _size);if (_size + 1 > _capacity){reserve(_size + 1);}for (size_t i = _size + 1; i > pos; --i)//要注意避免i减为-1变成一个极大的无符号数,导致死循环{_str[i] = _str[i - 1];}_str[pos] = c;++_size;return *this;}

2.插入一个字符串

在字符串某位置插入一个字符串:

		string& insert(size_t pos, const char* str){assert(pos <= _size);size_t i = 0;size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}for (i = _size + len; i > pos + len - 1; --i){_str[i] = _str[i - len];}strncpy(_str + pos, str, len);_size += len;return *this;}

6.erase

删除从字符串某位置开始,某长度的子串(如果不说明删除子串的长度,则默认从开始位置到最后的所有内容都删除)

		// 删除pos位置上的元素string& erase(size_t pos, size_t len = npos){if (len == npos || pos + len > _size){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;}

五、capacity

1.size

类外成员不能直接访问类的私有属性/数据,因此需要提供一个接口进行访问数据,但是要对数据进行保护,因此用了const传参和传值返回。
访问string中的元素个数。

		size_t size()const{return _size;}

2.capacity

访问string的容量。

		size_t capacity()const{return _capacity;}

3.empty

判断字符串是否为空。

		bool empty()const{if (_size == 0){return true;}return false;}

4.resize

修改字符串的元素个数,可以尾插指定的字符,未指定则默认插入’\0’。

		void resize(size_t n, char c = '\0')//更改元素个数{if (n <= _size){_size = n;_str[_size] = '\0';}else{reserve(n);//先扩容,再加元素for (size_t i = _size; i < n; ++i){_str[i] = c;}_str[n] = '\0';_size = n;}}

5.reserve

修改字符串的容量。

		void reserve(size_t n)//更改容量大小{if (n > _capacity){char* temp = new char[n + 1];//先开空间再拷贝值(避免因为空间开辟失败的异常使_str原地址也丢失了)strcpy(temp, _str);delete[] _str;_str = temp;_capacity = n;}}

六、access

访问字符串中的字符,运算符[]重载。

1.普通对象的接口(可读可写)

		char& operator[](size_t index)//普通对象的接口(可读可写){assert(index < _size);return _str[index];}

2.const对象的接口(只读)

		const char& operator[](size_t index)const//const对象的接口(只读){assert(index < _size);return _str[index];}

七、relational operators

1.运算符<重载

		bool operator<(const string& s){size_t len = _size < s._size ? _size : s._size;for (size_t i = 0; i < len; ++i){if ((*this)[i] >= s[i]){return false;}return true;}}

2.运算符>重载

		bool operator>(const string& s){size_t len = _size < s._size ? _size : s._size;for (size_t i = 0; i < len; ++i){if ((*this)[i] <= s[i]){return false;}return true;}}

3.运算符<=重载

复用>。

		bool operator<=(const string& s){return !(*this > s);}

4.运算符>=重载

复用<。

		bool operator>=(const string& s){return !(*this < s);}

5.运算符==重载

复用<和>。

		bool operator==(const string& s){return (!((*this) < s)) && (!((*this) > s));}

6.运算符!=重载

复用==。

		bool operator!=(const string& s){return !((*this) == s);}

八、String operations

1.find

1.找字符

找一个字符第一次在字符串中出现的下标

		// 返回c在string中第一次出现的位置(下标)size_t find(char c, size_t pos = 0) const{assert(pos < _size);for (size_t i = 0; i < _size; ++i){if ((*this)[i] == c){return i;}}return npos;}

2.找子串

找一个子串第一次出现在字符串中的下标
复用C语言对字符串的操作——strstr函数(在一个字符串中找子串)

		// 返回子串s在string中第一次出现的位置(下标)size_t find(const char* s, size_t pos = 0) const{assert(pos < _size);const char* ptr = strstr(_str + pos, s);if (ptr == nullptr){return npos;}else{return ptr - _str;}}

也可以一个字符一个字符的遍历寻找:

		// 返回子串s在string中第一次出现的位置(下标)size_t find(const char* s, size_t pos = 0) const{assert(pos < _size);size_t i = 0;while (i < _size){int flag = 0;if ((*this)[i] == s[0]){size_t k = i;for (size_t j = 0; j < strlen(s); ++j, ++k){if ((*this)[k] != s[j]){flag = 1;}}if (flag == 0) return i;}i++;}return npos;}

2.c_str

返回字符串的地址

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

九、Non-member function overloads

1.流插入

	ostream& operator<<(ostream& _cout, const string& s){for (size_t i = 0; i < s.size(); i++){_cout << s[i];}return _cout;}

2.流提取

	istream& operator>>(istream& _cin, string& s){s.clear();//将原来s中的值清除char buff[128] = { '\0' };size_t i = 0;char ch = _cin.get();//这里不能使用getc或者scanf函数的原因是他们都是按照空格' '或者换行'\n'进行分割内容的,所以无法取到空格和换行,因此只能用get()while (ch != ' ' || ch != '\n'){if (i == 127)//缓存部分满了{s += buff;//将内容存入s,同时将缓存部分情况i = 0;}buff[i++] = ch;ch = _cin.get();}return _cin;}

十、私有属性

	private:char* _str;size_t _capacity;size_t _size;const static size_t npos = -1;//只有这个特例可以这样定义,其他static数据成员都要在类内声明,在类外定义。

总结

以上就是今天要讲的内容,本文介绍了作者自己实现的string类的相关类成员函数,如果文章中的内容有错误或者不严谨的部分,欢迎大家在评论区指出,也欢迎大家在评论区提问、交流。
最后,如果本篇文章对你有所启发的话,希望可以多多支持作者,谢谢大家!

相关文章:

C++之模拟实现string

文章目录前言一、包含的相关头文件二、构造和析构1.构造函数2.拷贝构造1.传统写法2.现代写法3.赋值运算符重载1.传统写法2.现代写法4.析构函数三、iterator四、modify1.push_back(尾插一个字符&#xff09;2.append&#xff08;尾插一个字符串&#xff09;3.运算符重载1.尾插字…...

SpringBoot实战(十三)集成 Admin

目录一、简介二、搭建 springboot-admin 管理服务1.Maven 依赖2.application.yml3.添加 EnableAdminServer4.启动服务&#xff0c;查看页面三、搭建 springboot-admin-client 客户端服务1.Maven 依赖2.application.yml3.启动服务&#xff0c;查看页面四、搭配 Eureka 使用1.搭建…...

mke2fs命令:建立ext2文件系统

以下内容源于网络资源的学习与整理&#xff0c;如有侵权请告知删除。 使用格式 mke2fs [options] [设备名称] [区块数] options与含义 -c&#xff1a;检查是否有损坏的区块。-F&#xff1a;不管指定的设备为何&#xff0c;强制执行mke2fs。-M&#xff1a;记录最后一次挂入的…...

免费分享一个springboot+vue的办公系统

springbootvue的OA系统项目介绍项目部署项目特点项目展示项目介绍 这是一个采用前后端分离开发的项目&#xff0c;前端采用 Vue 开发、后端采用 SpringBoot Mybatis 开发。 很适合java初学者练手和学习。 前端技术&#xff1a;Vue3.2 Vue-Router Pinia Ant Design Vue 3.X…...

STM32数据搬运工DMA

DMA的概念DMA&#xff0c;全称为&#xff1a;Direct Memory Access&#xff0c;即直接存储器访问。DMA 传输方式无需 CPU 直接控制传输&#xff0c;也没有中断处理方式那样保留现场和恢复现场的过程&#xff0c;通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路&#xff…...

4、操作系统——进程间通信(2)(system V-IPC介绍)

目录 一、system V-IPC常识 1、key和ID 2、文件描述符 3、函数&#xff08;ftok&#xff09; ftok产生IPC对象的健值key&#xff08;类似文件路径&#xff09; 4、例子 5、使用命令查看或删除当前系统中的IPC对象 一、system V-IPC常识 1、key和ID &#xff08;1&#x…...

基于CentOS Stream 9平台搭建Nacos2.0.4集群以及OpenResty反向代理

目录展示Nacos2.0.4集群搭建1. 下载2. 解压3.修改配置3.1分别修改下启动类中JDK路径以及启动大小3.2 分别配置数据源3.3 创建nacos数据库3.4 修改cluster.conf配置3.4.1 复制并修改3.4.2 编辑文件&#xff0c;修改三台主机地址3.4.3 分别放入另外两个nacos的conf目录下:4. 启动…...

老杜MySQL入门基础 第二天

导入演示数据 1、连接MySQL 2、创建"bjpowernode"数据库 create database bjpowernode;3、选择数据库 use bjpowernode4、导入数据 source D&#xff1a;\bjpowernode.sql(文件的路径)1 去除重复记录(把查询结果去除重复记录)(原表数据不会改变) 使用关键字dist…...

Python深度学习实战:人脸关键点(15点)检测pytorch实现

引言 人脸关键点检测即对人类面部若干个点位置进行检测&#xff0c;可以通过这些点的变化来实现许多功能&#xff0c;该技术可以应用到很多领域&#xff0c;例如捕捉人脸的关键点&#xff0c;然后驱动动画人物做相同的面部表情&#xff1b;识别人脸的面部表情&#xff0c;让机…...

linux简单入门

目录Linux简介Linux目录结构Linux文件命令文件处理命令文件查看命令常用文件查看命令Linux的用户和组介绍Linux权限管理Linux简介 Linux&#xff0c;全称GNU/Linux&#xff0c;是一种免费使用和自由传播的类UNIX操作系统&#xff0c;其内核由林纳斯本纳第克特托瓦兹&#xff0…...

给准备面试网络工程师岗位的应届生一些建议

你听完这个故事&#xff0c;应该会有所收获。最近有一个23届毕业的大学生和我聊天&#xff0c;他现在网络工程专业大四&#xff0c;因为今年6、7月份的时候毕业&#xff0c;所以现在面临找工作的问题。不管是现在找一份实习工作&#xff0c;还是毕业后找一份正式工作&#xff0…...

主线程与子线程之间相互通信(HandlerThread)

平时&#xff0c;我们一般都是在子线程中向主线程发送消息&#xff08;要在主线程更新UI&#xff09;&#xff0c;从而完成请求的处理。那么如果需要主线程来向子线程发送消息&#xff0c;希望子线程来完成什么任务。该怎么做&#xff1f;这就是这篇文章将要讨论的内容。 一、…...

13基于双层优化的电动汽车日前-实时两阶段市场竞标

MATLAB代码&#xff1a;基于双层优化的电动汽车日前-实时两阶段市场竞标 关键词&#xff1a;日前-实时市场竞标 电动汽车 双层优化 编程语言&#xff1a;MATLAB平台 参考文献&#xff1a;考虑电动汽车可调度潜力的充电站两阶段市场投标策略_詹祥澎 内容简介&#xff1a;…...

REDIS19_zipList压缩列表详解、快递列表 - QuickList、跳表 - SkipList

文章目录①. 压缩列表 - zipList②. 快递列表 - QuickList③. 跳表 - SkipList①. 压缩列表 - zipList ①. ZipList是一种特殊的"双端链表",由一系列特殊编码的连续内存块组成。可以在任意一端进行压入/弹出操作,并且该操作的时间复杂度为O(1) (oxff:11111111) type…...

JavaScript 基础 - 第3天

文章目录JavaScript 基础 - 第3天笔记数组数组的基本使用定义数组和数组单元数据单元值类型数组长度属性操作数组JavaScript 基础 - 第3天笔记 数组 数组的基本使用 定义数组和数组单元 <script>// 1. 语法&#xff0c;使用 [] 来定义一个空数组// 定义一个空数组let…...

23.3.26总结

康托展开 是一个全排列与自然数的映射关系&#xff0c;康托展开的实质是计算当前序列在所有从小到大的全排列中的顺序&#xff0c;跟其逆序数有关。 例如&#xff1a;对于 1,2,3,4,5 来说&#xff0c;它的康托展开值为 0*4&#xff01;0*3&#xff01;0*2&#xff01;0*1&…...

【Java学习笔记】37.Java 网络编程

Java 网络编程 网络编程是指编写运行在多个设备&#xff08;计算机&#xff09;的程序&#xff0c;这些设备都通过网络连接起来。 java.net 包中 J2SE 的 API 包含有类和接口&#xff0c;它们提供低层次的通信细节。你可以直接使用这些类和接口&#xff0c;来专注于解决问题&…...

Azure OpenAI 官方指南03|DALL-E 的图像生成功能与安全过滤机制

2021年1月&#xff0c;OpenAI 推出 DALL-E。这是 GPT 模型在图像生成方面的人工智能应用。其名称来源于著名画家、艺术家萨尔瓦多 • 达利&#xff08;Dal&#xff09;和机器人总动员&#xff08;Wall-E&#xff09;。DALL-E 图像生成器&#xff0c;能够直接根据文本描述生成多…...

【数据结构】堆

文章目录前言堆的概念及结构堆初始化堆的判空堆的销毁插入数据删除数据堆的数据个数获取堆顶数据用数组创建堆对数组堆排序有关topk问题整体代码展示写在最后前言 &#x1f6a9;前面了解了树&#xff08;-> 传送门 <-&#xff09;的概念后&#xff0c;本章带大家来实现一…...

电脑硬盘文件数据误删除/格式化为什么可以恢复? 怎么恢复?谈谈文件删除与恢复背后的原理

Hello 大家好&#xff0c; 我是元存储~ 主页&#xff1a;元存储的博客_CSDN博客 1. 硬盘数据丢失场景 我们在每天办公还是记录数据的时候&#xff0c;文件存储大多数都是通过硬盘进行存储的&#xff0c;因此&#xff0c;使用多了&#xff0c;各种问题就会出现&#xff0c;比如…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用

中达瑞和自2005年成立以来&#xff0c;一直在光谱成像领域深度钻研和发展&#xff0c;始终致力于研发高性能、高可靠性的光谱成像相机&#xff0c;为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...