C++之“string”类的模拟实现
🌹个人主页🌹:喜欢草莓熊的bear
🌹专栏🌹:C++入门

前言
hello ,大家又来跟着bear学习了。一起奔向更好的自己,上篇博客已经讲清楚了string的一些功能的使用。我们就实现一些主要的功能。
一、实现string的思路

头文件
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
#include<assert.h>
using namespace std;
我们先写一个大框架,我的是放在了rose这个命名空间里面。定义完了成员变量,还给了缺省值。
namespace rose
{class string
{
public:private:
char* _str=nullptr;
size_t _size=0;
size_t _capacity=0;
}}
因为成员变量不是自定义类型,所以我们需要自己写构造和析构函数。
构造函数
如果没有传参数,我们就给一个”“,有的话我们就拷完成深拷贝,浅拷贝会导致内存问题。再原有基础上多开一个空间是方便储存\0。
string(const char* str="")//构造函数
{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];//储存\0strcpy(_str, str);//参数1是destination(目标) 参数2是source(源)
}//还是一样深拷贝
string(const string& s)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}
析构函数
我们释放我们之前申请的空间,如果申请了就释放,没有就不释放空间。
~string()//析构
{if (_str){delete[] _str;_str = nullptr;_size = _capacity = 0;}
}
size和capacity
很简单不用多说
size_t size()const//元素个数
{return _size;
}size_t capacity()const//容量
{return _capacity;
}
观察发现string的这些接口都是使用迭代器的,所以我们使用typedef 重定义一下 char* 和 const char*。

typedef char* iterator;//重定义
typedef const char* const_iterator;
begin和end
begin返回最开始的位置也将是首元素,end是最后一个元素。std库里面提供了两种我们也实现两种。
iterator begin()//因为成员函数,所以第一个参数不用写默认指向this指针
{return _str;
}const_iterator begin()const
{return _str;
}iterator end()
{return _str + _size;
}const_iterator end()const
{return _str + _size;
}
拷贝构造函数
浅拷贝会导致内存释放问题,所以还是采用深拷贝。
string(const string& s)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}
clear和c_str
clear是清除动态数组里面的字符,只剩\0,c_str是返回c语言风格的。


这两个也很简单实现
void clear()//清楚字符串
{_str[0] = '\0';_size = 0;
}const char* c_str() const//就是单纯返回C语言的风格,比如string类型直接使用strcmp不安全。
{return _str;
}
重载operator = 和 operator []
先看库里面string的”=“ 与 ”[]“。


重载[]也就是返回下标元素,注意的是要传合法下标(注意一下越界)
char& operator[](size_t pos)//【】
{assert(pos < _size);return _str[pos];
}const char& operator[](size_t pos)const
{assert(pos < _size);return _str[pos];
}
重载”=“分为两种一个是传统写法,还有一个现代写法。
传统写法就是什么都考自己来完成,我们是举例把s2赋值给s1。
先把之前原来数组里面的空间给释放,再重新申请s2的空间+1为了储存\0。接着把s2的成员变量拷贝给s1的成员变量.空间大小申请好了我们就把s2的字符复制给s1.这里调用strcpy。再判断一下s1和s2是否相等。
传统赋值
s1 = s2
string& operator=(const string& s)
{if (this != &s)//this指针是地址所以需要取s的地址来比较{delete[]_str;_str = new char[s._capacity + 1];_size = s._size;_capacity = s._capacity;strcpy(_str, s._str);}return *this;
}
现代写法,大部分考编译器来做。
借助拷贝构造完成中间对象的创建,并且把中间对象和this指针的对象进行交换(指向的空间,数据个数,容量)又因为tmp出来作用域会销毁所以,还顺便把this之前的空间也给销毁了。
void swap(string& str)
{std::swap(_str, str._str);//std::swap是库里面的std::swap(_size, str._size);std::swap(_capacity, str._capacity);
}//借助拷贝构造完成中间对象的创建,并且把中间对象和this指针的对象进行交换(指向的空间,数据个数,容量)
//又因为tmp出来作用域会销毁所以,还顺便把this之前的空间也给销毁了。
string& operator=(const string& s)//现代赋值
{if (this != &s)//this指针是地址所以需要取s的地址来比较{string tmp(s);swap(tmp);}return *this;
}//s1 = s2
string& operator=(string tmp)//直接使用临时对象来接收s2的值//因为是传值传参使用会调用拷贝构造//这里判断他们相等意义不大,可判 可不判
{swap(tmp);return *this;
}
我们这些写的都是内联函数,频繁调用且代码不长的。
reserve、push_back和append
reserve:目的扩容,首先判断需不需要扩容。我们采用深拷贝,把原数组里面的内容cpy到临时数组tmp里面。
push_back:目的插入字符,分为容量有没有满,没满就直接尾插,满了扩容后继续尾插。一般扩容是按照两倍来的。
append:目的是在末尾加入字符串,和push_back一样是否需要扩容,因为是字符串所以我们采用strcpy更快,但是初始位置是_str+size。
void string::reserve(size_t n = 0)//扩容函数
{if (n > _capacity){char* tmp = new char[n + 1];//多一个用来装\0strcpy(tmp, _str);delete[]_str;_str = tmp;_capacity = n;}
}void string::push_back(char ch)
{if (_capacity = _size){reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size++] = ch;_str[_size] = '\0';
}void string::append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){// 大于2倍,需要多少开多少,小于2倍按2倍扩reserve(_size+len > 2 * _capacity ? _size + len :2*_capacity);}strcpy(_str + _size, str);//因为append是要实现在字符串后面加字符串,这里是_str(数组首元素)+_size,正好拷贝的时候覆盖率旧的\0_size += len;
}
insert和erase
insert:这里是在pos位置插入一个字符。
erase:是删除从第pos位置后面的所有字符。
void string::insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}for (size_t i = 0;i < len;i++)//{_str[pos + i] = str[i];}_size += len;
}void string::erase(size_t pos = 0, size_t len = npos)
{assert(pos < _size);if (len > _size - pos){_str[pos] = '\0';_size = pos;}else{for (size_t i = len + pos;i <= _size;i++){_str[i - len] = _str[i];--_size;}//_size-=len;}
}
find和substr
find写了两个,一个是查找第一个字符,还有查找字符串。
substr:拷贝pos到len指定区域的字符。
size_t string::find(char ch, size_t pos)
{assert(pos < _size);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)
{assert(pos < _size);const char* tmp = strstr(_str + pos, str);//str是比较字符串的函数。if (tmp == nullptr){return npos;}else{return tmp - _str;//?}}string string::substr(size_t pos, size_t len)//拷贝区间字符
{assert(pos < _size);if (len > _size - pos){len = _size - pos;}string sub;sub.reserve(len);for (size_t i = 0;i < len;i++){sub[i] += _str[pos + i];}return sub;
}
operator+=
分为+=字符和字符串,可以直接调用插入函数。
string& string::operator+=(char ch)//这个函数是string类的呀,用域名修饰符修饰了,对的,这个函数实际还是一个成员函数,
{push_back(ch);return *this;//
}
string& string::operator+=(const char* str)
{append(str);return *this;//
}
operator <、>、==、<= 、>=、!=
我们可以通过实现其中两个来通过他们的逻辑关系来实现其他的。
我这里是实现了operator<和operator==,调用strcmp来实现的。
bool operator<(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) < 0;
}bool operator==(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) == 0;
}bool operator<=(const string& s1, const string& s2)
{return s1 < s2 || s1 == s2;
}
bool operator>(const string& s1, const string& s2)
{return !(s1 <= s2);
}
bool operator>=(const string& s1, const string& s2)
{return !(s1 < s2);
}bool operator!=(const string& s1, const string& s2)
{return !(s1 == s2);
}
operator << 和 >>
operator<< 函数接受一个 ostream 类型的对象作为参数,并返回该对象本身。它遍历输入的字符串中的每个字符,并将其写入到输出流中。
operator>> 函数则接受一个 istream 类型的对象作为参数,并返回该对象本身。它首先清空输入字符串,然后逐个读取输入流中的字符,直到遇到空格或换行符为止。如果在读取过程中遇到了超过 N-1 个字符,则会将前面的部分存储到缓冲区中,并将剩余部分直接存储到输入字符串中。最后,它将读取到的所有字符存储到输入字符串中并返回输入流本身。
ostream& operator<<(ostream& out, const string& s)
{for (auto ch : s){out << ch;}return out;
}
istream& operator>>(istream& in, string& s)//
{s.clear();const int N = 256;char buff[N];int i = 0;char ch;//in >> ch;//使用in>>ch 默认空格为分格符读取不到ch = in.get();//给次对一个字符,什么都读。while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}//in >> ch;ch = in.get();}if (i > 0)//提前遇到“ ”或者 “\n”;{buff[i] = '\0';s += buff;}return in;
}
总结
感谢大家的支持,我会继续努力创造出更好的博客。
相关文章:
C++之“string”类的模拟实现
🌹个人主页🌹:喜欢草莓熊的bear 🌹专栏🌹:C入门 前言 hello ,大家又来跟着bear学习了。一起奔向更好的自己,上篇博客已经讲清楚了string的一些功能的使用。我们就实现一些主要的功…...
请谈谈 HTTP 中的安全策略,如何防范常见的Web攻击(如XSS、CSRF)?
一、Web安全核心防御机制 (一)XSS攻击防御(跨站脚本攻击) 1. 原理与分类 存储型XSS:恶意脚本被持久化存储在服务端(如数据库)反射型XSS:脚本通过URL参数或表单提交触发执行…...
Python Flask 渲染静态程动态页面
Python Flask 渲染静态程动态页面 Python Flask 渲染静态程动态页面 Python Flask 渲染静态程动态页面 对网页应用程序来说,静态内容是重要的,因为它们包括 CSS 和 JavaScript 文件。静态文件可以直接由网页服务器提供。如果我们在我们的项目中创建一个…...
Unity大型游戏开发全流程指南
一、开发流程与核心步骤 1. 项目规划与设计阶段 需求分析 明确游戏类型(MMORPG/开放世界/竞技等)、核心玩法(战斗/建造/社交)、目标平台(PC/移动/主机)示例:MMORPG需规划角色成长树、副本Boss…...
Unity场景制作
一、关于后处理效果 然后可在后处理组件中添加各种效果 ACES : 电影感的强对比效果 添加了ACES后场景明显变暗,所以可以提高曝光度 Post-exposure 二、添加雾效 在Window的项目栏中选择Render中的Lighting 在环境属性中的其他设置中可勾选雾效,为场景中添…...
PCIE接口
PCIE接口 PIC接口介绍PIC总线结构PCI总线特点PCI总线的主要性能PIC的历程 PCIE接口介绍PCIe接口总线位宽PCIE速率GT/s和Gbps区别PCIE带宽计算 PCIE架构PCIe体系结构端到端的差分数据传递PCIe总线的层次结构事务层数据链路层物理层PCIe层级结构及功能框图 PCIe链路初始化PCIe链路…...
Leetcode 3479. Fruits Into Baskets III
Leetcode 3479. Fruits Into Baskets III 1. 解题思路2. 代码实现 题目链接:3479. Fruits Into Baskets III 1. 解题思路 这一题思路本质上就是考察每一个水果被考察时找到第一个满足条件且未被使用的basket。 因此,我们只需要将basket按照其capacit…...
小程序 -- uni-app开发微信小程序环境搭建(HBuilder X+微信开发者工具)
目录 前言 一 软件部分 1. 微信开发者工具 2. HBuilder X 开发工具 二 配置部分 1. 关于 HBuilder X 配置 2. 关于 微信开发工具 配置 三 运行项目 1. 新建项目 2. 代码编写 3. 内置浏览器 编译 4. 配置小程序 AppID获取 注意 四 实现效果 前言 uni-app开发小程…...
深度学习PyTorch之13种模型精度评估公式及调用方法
深度学习pytorch之22种损失函数数学公式和代码定义 深度学习pytorch之19种优化算法(optimizer)解析 深度学习pytorch之4种归一化方法(Normalization)原理公式解析和参数使用 深度学习pytorch之简单方法自定义9类卷积即插即用 实时…...
《云原生监控体系构建实录:从Prometheus到Grafana的观测革命》
PrometheusGrafana部署配置 Prometheus安装 下载Prometheus服务端 Download | PrometheusAn open-source monitoring system with a dimensional data model, flexible query language, efficient time series database and modern alerting approach.https://prometheus.io/…...
GHCTF2025--Web
upload?SSTI! import os import refrom flask import Flask, request, jsonify,render_template_string,send_from_directory, abort,redirect from werkzeug.utils import secure_filename import os from werkzeug.utils import secure_filenameapp Flask(__name__)# 配置…...
NO.32十六届蓝桥杯备战|函数|库函数|自定义函数|实参|形参|传参(C++)
函数是什么 数学中我们其实就⻅过函数的概念,⽐如:⼀次函数 y kx b ,k和b都是常数,给⼀个任意的x ,就得到⼀个 y 值。其实在C/C语⾔中就引⼊了函数(function)的概念,有些翻译为&a…...
计算机视觉算法实战——老虎个体识别(主页有源码)
✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ 1. 领域介绍 老虎个体识别是计算机视觉中的一个重要应用领域,旨在通过分析老虎的独特条纹图案,自动识别和区…...
【移动WEB开发】rem适配布局
目录 1. rem基础 2.媒体查询 2.1 语法规范 2.2 媒体查询rem 2.3 引入资源(理解) 3. less基础 3.1 维护css的弊端 3.2 less介绍 3.3 less变量 3.4 less编译 3.5 less嵌套 3.6 less运算 4. rem适配方案 4.1 rem实际开发 4.2 技术使用 4.3 …...
25年携程校招社招求职能力北森测评材料计算部分:备考要点与误区解析
在求职过程中,能力测评是筛选候选人的重要环节之一。对于携程这样的知名企业,其能力测评中的材料计算部分尤为关键。许多求职者在备考时容易陷入误区,导致在考试中表现不佳。本文将深入解析材料计算部分的实际考察方向,并提供针对…...
【Elasticsearch入门到落地】9、hotel数据结构分析
接上篇《8、RestClient操作索引库-基础介绍及导入demo》 上一篇我们介绍了RestClient的基础,并导入了使用Java语言编写的RestClient程序Demo以及将要分析的数据库。本篇我们就要分析导入的宾馆数据库tb_hotel表结构的具体含义,并分析如何建立其索引库。 …...
现代互联网网络安全与操作系统安全防御概要
现阶段国与国之间不用对方路由器,其实是有道理的,路由器破了,内网非常好攻击,内网共享开放端口也非常多,更容易攻击。还有些内存系统与pe系统自带浏览器都没有javascript脚本功能,也是有道理的,…...
轻量级TCC框架的实现
现有seata、tcc-transaction等tcc框架实现都较为重量级,今天主要带来一种轻量级的实现,大概只用了1200行代码实现。不依赖具体框架grpc、http、dubbo等,只需要业务系统按照标准化实现Try、Commit、Cancel实现接口即可。 已解决悬挂、幂等、空…...
共绘智慧升级,看永洪科技助力由由集团起航智慧征途
在数字化洪流汹涌澎湃的当下,企业如何乘风破浪,把握转型升级的黄金机遇,已成为所有企业必须直面的时代命题。由由集团,作为房地产的领航者,始终以前瞻视野引领变革,坚决拥抱数字化浪潮,携手数字…...
小程序开发总结
今年第一次帮别人做小程序。 从开始动手到完成上线,一共耗时两天。AI 让写代码变得简单、高效。 不过,小程序和 Flutter 等大厂开发框架差距实在太大,导致我一开始根本找不到感觉。 第一,IDE 不好用,各种功能杂糅在…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...
从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...

