(C++ STL)string类的简单模拟实现与源码展示

string类的简单模拟实现

  • 一、前言
  • 二、string 的成员变量
  • 三、string 的主要部分实现
    • reserve
    • Iterator
    • operator[]
    • insert
    • erase
    • find
    • resize
    • 构造、析构、赋值重载
  • 四、string 源代码
    • 在 string.h 中:
    • 在 string.cpp 中:

一、前言

C++ 为了更安全、简单的使用字符串,对 C语言 中的字符串和相关函数进行封装,实现了 string 类。接下来将会实现 string 与一部分常用的函数。

参考:legacy.cplusplus.com网站的 string

具体实现:

  1. string部分构造函数、析构函数、赋值运算符重载。
  2. Iterator 中的 begin、end。
  3. Capacity 中的 size、resize、capacity、reserve、clear、empty。
  4. Element access 中的 operator[]。
  5. Modifiers 中的 operator+=、append、push_back、insert、erase、swap、pop_back 。
  6. String operations 中的 c_str、find。
  7. Non-member function overload 中的 relational operators、operator>>、operator<<。

二、string 的成员变量

在 string.h 中:

namespace my
{class string{public:static const size_t npos = -1;private:char* _str;size_t _capacity;size_t _size;};
}
  1. npos 表示的是 string 的结束位子,详细可参考:C++中string::npos的一些用法总结
  2. _str 字符指针用来存储 在堆上开辟的 字符串数据的 地址。
  3. _capacity 表示容器空间大小,因为 C语言 字符串末尾隐藏了一个 ’ \0 ',则真实存储字符的容量为 _capacity - 1
  4. _size 表示当前字符串大小(不含 ’ \0 ')。

三、string 的主要部分实现

考虑到代码复用,实现并不按照上述排列顺序,且只列举一部分重要的功能。

reserve

参考:std::string::reserve

C++ 对 string reserve函数规定:

  1. 原空间大小 小于 需求大小 时, 对原空间扩容到需求大小或其以上。
  2. 当 原空间 大于或等于 需求 时,非必要的缩减空间调整。

这里实现的空间增长为严格的 2 倍扩容,对于 序号 2 的情况采取直接忽视。

在 string.cpp 中:

void my::string::reserve(size_t n)
{if (n + 1 <= _capacity)			// 考虑末尾 '\0'{return;}size_t newCapacity = _capacity == 0 ? MY_STRING_INIT_CAPACITY : _capacity * 2;// n 仍然大于newCapacity时继续扩容// newCapacity <= n 等价 newCapacity < n + 1while (newCapacity <= n)	{newCapacity <<= 1;}char* str1 = nullptr;			// str1存储原空间地址	std::swap(str1, _str);	_str = new char[newCapacity];	// _str 新开空间后拷贝// _size >= _capacity 只出现在初始化时(这时 _size == _capacity == 0)// 以防止超出 _capacity 的非法内存访问memcpy(_str, str1, sizeof(char) * (_size >= _capacity ? _capacity : _size + 1));_capacity = newCapacity;delete[] str1;
}

Iterator

这里迭代器只对原生 char* 进行封装。

参考:std::string::begin
参考:std::string::end

这里只实现部分。

在 my::string 中:

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

operator[]

参考:std::string::operator[]

在 my::string 类中

        char& operator[](size_t index){return *(_str + index);}const char& operator[](size_t index) const{return *(_str + index);}

insert

这里只实现两个。

参考:std::string::insert

在 string.cpp 中:

// 在 pos 位置上插入 字符ch / 字符串str,并返回该string
my::string& my::string::insert(size_t pos, char ch)
{assert(pos <= _size);reserve(++_size);// 将 pos 原位置与之后字符向后移动一步,再用 ch 覆盖 pos 当前原字符for (size_t end = _size; end >= pos + 1; --end){_str[end] = _str[end - 1];}_str[pos] = ch;return *this;
}my::string& my::string::insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);if (len != 0){reserve(_size += len);// 将 pos 原位置与之后字符向后移动len步,再用 str 覆盖 pos 和之后原字符串for (size_t end = _size; end >= pos + len; --end){_str[end] = _str[end - len];}strncpy(_str + pos, str, len);}return *this;
}

erase

这里只实现一个。

参考:std::string::erase

在 string.cpp 中:

// 删除 pos ~ pos + len 位置上的元素,并返回该 string
my::string& my::string::erase(size_t pos, size_t len)
{assert(pos < _size);if (len > _size)		// len 默认值为 npos, 此时不能加,会溢出{len = _size;}// 若后面还有数据需移动for (size_t end = pos; end < _size - len; ++end){_str[end] = _str[end + len];}_size = (_size - pos <= len ? pos : _size - len);_str[_size] = '\0';		// '\0' 需单独处理return *this;
}

find

这里只实现两个。

参考:std::string::find

在 string.cpp 中:

// 返回 ch 在 string 中第一次出现的位置
size_t my::string::find(char ch, size_t pos) const
{assert(pos <= _size);size_t index = pos;while (index < _size){if (_str[index] == ch){break;}++index;}return index == _size ? npos : index;
}// 返回子串 str1 在 string 中第一次出现的位置
size_t  my::string::find(const char* str1, size_t pos) const
{size_t len1 = strlen(str1);assert(pos + len1 <= _size);size_t index = pos;while (index + len1 <= _size){if (strncmp(_str + index, str1, len1) == 0){break;}++index;}return index + len1 <= _size ? index : npos;
}

resize

参考:std::string::resize

在 my::string 中:

        void resize(size_t n, char ch = '\0'){if (n > _size){reserve(n);for (int i = _size + 1; i < n; ++i){_str[i] = ch;}}_str[_size = n] = '\0';}

构造、析构、赋值重载

参考:std::string::string
参考:std::string::~string
参考:std::string::operator=

在 my::string 中:

        string(const char* str1 = ""):_str(nullptr),_size(0),_capacity(0){size_t len1 = strlen(str1);reserve(len1);memcpy(_str, str1, sizeof(char) * (len1 + 1));_size = len1;}string(const string& str1):_str(nullptr),_size(0),_capacity(0){reserve(str1.capacity());memcpy(_str, str1._str, sizeof(char) * (str1._size + 1));_size = str1._size;}~string(){if (_str != nullptr){delete[] _str;_str = nullptr;}_size = _capacity = 0;}void swap(string& str1){std::swap(_str, str1._str);std::swap(_size, str1._size);std::swap(_capacity, str1._capacity);}string& operator=(string str1){swap(str1);return *this;}

四、string 源代码

在 string.h 中:

#pragma once#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>
#include <cassert>#define MY_STRING_INIT_CAPACITY 4namespace my
{class string{friend std::ostream& operator<<(std::ostream& cout, const my::string& str1);friend std::istream& operator>>(std::istream& cin, my::string& str1);public:typedef char* iterator;public:string(const char* str1 = ""):_str(nullptr),_size(0),_capacity(0){size_t len1 = strlen(str1);reserve(len1);memcpy(_str, str1, sizeof(char) * (len1 + 1));_size = len1;}string(const string& str1):_str(nullptr),_size(0),_capacity(0){reserve(str1.capacity());memcpy(_str, str1._str, sizeof(char) * (str1._size + 1));_size = str1._size;}~string(){if (_str != nullptr){delete[] _str;_str = nullptr;}_size = _capacity = 0;}void swap(string& str1){std::swap(_str, str1._str);std::swap(_size, str1._size);std::swap(_capacity, str1._capacity);}string& operator=(string str1){swap(str1);return *this;}//-----------------------------------------------------------iterator begin(){return _str;}iterator end(){return _str + _size;}//-----------------------------------------------------------void push_back(char ch){insert(_size, ch);}void pop_back(){erase(_size - 1, 1);}string& operator+=(char ch){return insert(_size, ch);}void append(const char* str1){insert(_size, str1);}string& operator+=(const char* str1){return insert(_size, str1);}void clear(){_str[_size = 0] = 0;}const char* c_str() const{return _str;}//-----------------------------------------------------------size_t size() const{return _size;}size_t capacity() const{return _capacity - 1;}bool empty() const{return _size == 0;}void resize(size_t n, char ch = '\0'){if (n > _size){reserve(n);for (int i = _size + 1; i < n; ++i){_str[i] = ch;}}_str[_size = n] = '\0';}void reserve(size_t n);//-----------------------------------------------------------char& operator[](size_t index){return *(_str + index);}const char& operator[](size_t index) const{return *(_str + index);}//-----------------------------------------------------------bool operator<(const string& str1){return strcmp(c_str(), str1.c_str()) < 0;}bool operator>(const string& str1){return strcmp(c_str(), str1.c_str()) > 0;}bool operator==(const string& str1){return strcmp(c_str(), str1.c_str()) == 0;}bool operator<=(const string& str1){return !(*this > str1);}bool operator>=(const string& str1){return !(*this < str1);}bool operator!=(const string& str1){return !(*this == str1);}//-----------------------------------------------------------size_t find(char ch, size_t pos = 0) const;size_t find(const char* str1, size_t pos = 0) const;string& insert(size_t pos, char ch);string& insert(size_t pos, const char* str);string& erase(size_t pos = 0, size_t len = npos);static const size_t npos = -1;private:char* _str;size_t _capacity;size_t _size;};
}

在 string.cpp 中:

#include "string.h"void my::string::reserve(size_t n)
{if (n + 1 <= _capacity)			// 考虑末尾 '\0'{return;}size_t newCapacity = _capacity == 0 ? MY_STRING_INIT_CAPACITY : _capacity * 2;// n 仍然大于newCapacity时继续扩容// newCapacity <= n 等价 newCapacity < n + 1while (newCapacity <= n)	{newCapacity <<= 1;}char* str1 = nullptr;			// str1存储原空间地址	std::swap(str1, _str);	_str = new char[newCapacity];	// _str 新开空间后拷贝// _size >= _capacity 只出现在初始化时(这时 _size == _capacity == 0)// 以防止超出 _capacity 的非法内存访问memcpy(_str, str1, sizeof(char) * (_size >= _capacity ? _capacity : _size + 1));_capacity = newCapacity;delete[] str1;
}// 返回c在string中第一次出现的位置
size_t my::string::find(char ch, size_t pos) const
{assert(pos <= _size);size_t index = pos;while (index < _size){if (_str[index] == ch){break;}++index;}return index == _size ? npos : index;
}// 返回子串str1在string中第一次出现的位置
size_t  my::string::find(const char* str1, size_t pos) const
{size_t len1 = strlen(str1);assert(pos + len1 <= _size);size_t index = pos;while (index + len1 <= _size){if (strncmp(_str + index, str1, len1) == 0){break;}++index;}return index + len1 <= _size ? index : npos;
}// 在 pos 位置上插入 字符ch / 字符串str,并返回该string
my::string& my::string::insert(size_t pos, char ch)
{assert(pos <= _size);reserve(++_size);// 将 pos 原位置与之后字符向后移动一步,再用 ch 覆盖 pos 当前原字符for (size_t end = _size; end >= pos + 1; --end){_str[end] = _str[end - 1];}_str[pos] = ch;return *this;
}my::string& my::string::insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);if (len != 0){reserve(_size += len);// 将 pos 原位置与之后字符向后移动len步,再用 str 覆盖 pos 和之后原字符串for (size_t end = _size; end >= pos + len; --end){_str[end] = _str[end - len];}strncpy(_str + pos, str, len);}return *this;
}// 删除 pos ~ pos + len 位置上的元素,并返回该 string
my::string& my::string::erase(size_t pos, size_t len)
{assert(pos < _size);if (len > _size)		// len 默认值为 npos, 此时不能加,会溢出{len = _size;}// 若后面还有数据需移动for (size_t end = pos; end < _size - len; ++end){_str[end] = _str[end + len];}_size = (_size - pos <= len ? pos : _size - len);_str[_size] = '\0';		// '\0' 需单独处理return *this;
}std::ostream& my::operator<<(std::ostream& out, const my::string& str1)
{out << str1.c_str();return out;
}std::istream& my::operator>>(std::istream& in, my::string& str1)
{str1.clear();// 缓冲char buf[100] = { 0 };char ch = in.get();int i = 0;while (ch != ' ' && ch != '\n'){buf[i++] = ch;if (i >= 99){str1 += buf;i = 0;}ch = in.get();}if (i > 0){buf[i] = '\0';str1 += buf;}return in;
}

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

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

相关文章

并查集【算法 12】

并查集 (Union-Find) 的基础概念与实现 并查集&#xff08;Union-Find&#xff09;是一种用于处理不相交集合&#xff08;disjoint sets&#xff09;的数据结构&#xff0c;常用于解决连通性问题。典型的应用场景包括动态连通性问题&#xff08;如网络节点连通性检测&#xff0…

【STM32】FMC

FMC功能与FSMC类似&#xff0c;但比FSMC更强大&#xff0c;但仅在F4 / F7 / H7等高级一点的MCU上支持&#xff0c;F1不支持。虽然我的是F103&#xff0c;但顺便都看了。 大部分图片来源&#xff1a;正点原子HAL库课程 专栏目录&#xff1a;记录自己的嵌入式学习之路-CSDN博客 目…

Axure 9 使用

一、界面初识 二、基础功能 1.菜单栏 1.1文件 新建文件&#xff1a;axure9包含四种文件.rp代表原型文件&#xff0c;.rplib代表元件库文件&#xff0c;.rpteam 团队项目文件 .html 网页文件 偏好设置&#xff1a;备份&#xff0c;需要备份文件再从备份中恢复 创建项目团…

二叉树前序,中序,后序非递归遍历(Java)

1. 思路&#xff1a; 首先创建一个栈和顺序表&#xff0c;按照根左右的前序遍历顺序去遍历这棵树&#xff0c;一直往左孩子方向遍历&#xff0c;每遍历到一个结点就入栈并且加入到顺序表里&#xff0c;如果没有左孩子了&#xff0c;就拿出栈顶元素&#xff0c;看它是否有右孩子…

智能学习辅助系统——后端部分

目录 前言 一、准备工作 1.需求&环境搭建 1.1需求说明 1.2环境搭建 2.开发规范 2.1 开发规范-REST 2.2 开发规范-统一响应结果 3.开发流程 二、部门管理 1.查询部门 &#xff08;1&#xff09;原型和需求 &#xff08;2&#xff09;接口文档 &#xff08;3&…

Unity2D游戏开发-Pak木鱼

在接下来文章里我会以Unity为主一起制作游戏 在unity 里如何制作一个简单的敲木鱼游戏&#xff1f; 创建一个2D场景&#xff08;本人使用Unity2023&#xff09; (每个一段时间要申请一个个人许可证) 点击下方蓝色按钮创建 将以下素材拖动到Assets文件夹中 这张图随意命名我…

深入理解DPO(Direct Preference Optimization)算法

目录 1. 什么是DPO&#xff1f;2. Bradley-Terry模型2.1 奖励模型的训练 3. 从PPO到DPO4. DPO的简单实现5. 梯度分析Ref 1. 什么是DPO&#xff1f; 直接偏好优化&#xff08;Direct Preference Optimization, DPO&#xff09;是一种不需要强化学习的对齐算法。由于去除了复杂的…

SQL Server中如何自动抓取阻塞

背景 当发数据库生阻塞时&#xff0c;可以通过SQL语句来获取当前阻塞的会话情况&#xff0c;可以得到下面的信息 说明&#xff1a;会话55阻塞了会话53。两个会话都执行了update test set fid10 where fid0。 但我们也经常碰到客户生产环境出现阻塞&#xff0c;由于不会抓取或者…

还在拼接字符串生成XML?(Java)

FreeMarker是一个功能强大的Java模板引擎&#xff0c;广泛应用于生成动态内容&#xff0c;如HTML、XML和其他文本格式。本文将介绍FreeMarker的基本使用方法&#xff0c;并提供一个更丰富的XML模板示例&#xff0c;以及模板标签和标识的含义。 1. 引入依赖 <dependency>…

三分钟总结开源流程表单的优势特点

实现流程化办公&#xff0c;可以借助低代码技术平台、开源流程表单的优势特点。作为当前较为理想的平台产品&#xff0c;低代码技术平台凭借够灵活、好操作、可视化界面的优势特点&#xff0c;得到了通信业、医疗、高校等很多行业客户朋友的喜爱与支持。今天一起来看看开源流程…

联华证券-新手炒股入门指南:学习路径与注意事项

学习炒股是一个循序渐进的过程&#xff0c;以下是入门建议以及需要注意的事项&#xff1a; 学习炒股的入手步骤 掌握基础知识&#xff1a; 股票市场基础&#xff1a;了解什么是股票、股市的运作机制、股票的种类等基本概念。 常用术语&#xff1a;熟悉如市盈率&#xff08;P/…

ET6框架(三)前后端通讯分析

文章目录 一、信息的通讯二、网络通讯协议的“理像模型”三、网络通讯协议的“四层模型”四、什么是 Socket&#xff1f;五、Socket通讯流程 一、信息的通讯 网络消息的发送类似于邮寄信件的流程&#xff0c;需要一个地址及收件人。 在网络通讯中通常我们需要一个IP地址及端口…

uni-app启动本地开发环境,修改默认端口号

vite.config.js: import { defineConfig } from "vite"; import uni from "dcloudio/vite-plugin-uni";// https://vitejs.dev/config/ export default defineConfig({server: {port: 3006,},plugins: [uni()], });人工智能学习网站 https://chat.xutong…

趣味算法------过河卒

目录 ​编辑 题目描述 解题思路 具体代码 总结 问题描述&#xff1a; 解决方案&#xff1a; 代码实现&#xff1a; 关键点&#xff1a; 题目描述 棋盘上 A 点有一个过河卒&#xff0c;需要走到目标 B 点。卒行走的规则&#xff1a;可以向下、或者向右。同时在棋盘上 C…

自动化通过cmd命令行并以特定账户连接到远程机器

1 建一个taskschedule运行cmd命令 2 cmd命令示例&#xff1a; 机器名加域名 mstsc /v:"<MachineName>.xxx.xxx.com"或者是机器IP地址 mstsc /v:"10.148.66.178"3 设置用特定账户登陆 用户名可以写 <要连接的机器名><用户名> 勾选“记住…

川崎机器人维修请开启马达电源报警故障

‌川崎机器人故障代码主要用于指示机器人的不同运行问题和状态&#xff0c;帮助快速识别和解决这些问题。‌Kasawaki机械手故障代码通常以字母和数字的组合形式出现&#xff0c;其中字母代表故障的类型&#xff0c;而数字则是具体的代码编号。这些代码可以分为‌P‌代表操作错误…

05:创建逻辑软件元件库

1.创建逻辑软件元件库 点击 “编辑电参数” 1.1常规设置 1.2PCB封装 1.3门 1.4管脚 1.5检查元件 点击确定 1.6点击保存 2.处理重叠问题 2.1查看处理后的显示

《JavaEE进阶》----5.<SpringMVC②剩余基本操作(CookieSession)>

Cookie和Session简介。 Spring MVC的请求中 Cookie的设置和两种获取方式 Session的设置和三种获取方式。 三、&#xff08;接上文&#xff09;SpringMVC剩余基本操作 3.2postman请求 3.2.10 获取Cookie和Session 1.理解Cookie 我们知道HTTP协议自身是“无状态”协议。 &qu…

XtQuant接口概述,想用miniQMT做量化哪家券商支持?

XtQuant.XtData 行情模块 xtdata是xtquant库中提供行情相关数据的模块&#xff0c;本模块旨在提供精简直接的数据满足量化交易者的数据需求&#xff0c;作为python库的形式可以被灵活添加到各种策略脚本中。 主要提供行情数据&#xff08;历史和实时的K线和分笔&#xff09;、…

《黑神话悟空》:国产3A游戏的崛起与AI绘画技术的融合

一、游戏简介 近年来&#xff0c;国产3A游戏《黑神话悟空》以其精美的画面、丰富的剧情和独特的文化底蕴吸引了众多玩家的关注。这款游戏以中国古典名著《西游记》为背景&#xff0c;讲述了孙悟空历经磨难&#xff0c;最终成长为斗战胜佛的故事。在游戏制作过程中&#xff0c;开…