探索C嘎嘎的奇妙世界:第十四关---STL(string的模拟实现)
1. string类的模拟实现
1.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 = ""){// 构造String类对象时,如果传递nullptr指针,可以认为程序非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;
};
// 测试
void TestString()
{String s1("hello bit!!!");String s2(s1);
} 
1.2 浅拷贝
1.3 深拷贝
2.string各类主要接口的模拟实现
2.1 迭代器
string.h:typedef char* iterator;typedef const char* const_iterator;iterator begin();//迭代器起始位置iterator end();//迭代器const_iterator begin()const;const_iterator end()const;string.cpp:string::iterator string::begin(){return _str;}string::iterator string::end(){return _str + _size;}string::const_iterator string::begin()const{return _str;}string::const_iterator string::end()const{return _str + _size;}
上述代码中定义了一个类`string`,该类具有`begin()`和`end()`函数,用于返回迭代器对象。
迭代器是一种用于遍历容器元素的对象。迭代器将容器中的元素组织起来,以便可以按顺序访问它们。
在`string.h`中,`typedef char* iterator;`和`typedef const char* const_iterator;`定义了两种迭代器类型,分别用于可变和常量的字符串。
`string::begin()`和`string::end()`函数分别返回迭代器的起始位置和结束位置。
在`string.cpp`中,`string::begin()`和`string::end()`函数被实现。`string::begin()`函数返回字符串的起始位置,即指向第一个字符的指针。`string::end()`函数返回字符串的结束位置,即指向最后一个字符后面的位置的指针。
`string::begin()const`和`string::end()const`函数是常量成员函数,用于返回常量字符串的迭代器的起始位置和结束位置。
通过使用这些迭代器,可以在循环中遍历字符串中的每个字符,并执行相应的操作。
2.2 size、c_str、运算符[ ]的重载、构造函数以及析构函数
string.h:string(const char* str = "");~string();size_t size()const;const char* c_str()const;char& operator[](size_t pos);const char& operator[](size_t pos)const;string.cpp:string::string(const char*str):_size(strlen(str)){_str=new char[_size+1];_capacity = _size;strcpy(_str, str);}string::~string(){delete[] _str;_str = nullptr;_capacity = _size = 0;}size_t string::size()const{return _size;}const char* string:: c_str()const{return _str;}char& string:: operator[](size_t pos){assert(pos < _size);return _str[pos];}const char& string:: operator[](size_t pos)const{assert(pos < _size);return _str[pos];}
上述代码中定义了一个类`string`,该类包含了几个常用的字符串相关函数。
在`string.h`中,构造函数`string(const char* str = "")`用于创建一个字符串对象。析构函数`~string()`用于销毁字符串对象并释放内存。`size_t size()const`函数用于返回字符串的长度`const char* c_str()const`函数用于返回字符串的C风格字符数组。
`char& operator[](size_t pos)`和`const char& operator[](size_t pos)const`函数分别用于访问字符串中指定位置的字符。`operator[]`函数设计为“下标运算符重载”,允许使用类似数组下标的方式来访问字符串中的字符。
在`string.cpp`中,构造函数`string::string(const char* str)`用于根据传入的C风格字符串创建一个新的字符串对象。函数内部首先计算传入的字符串的长度,然后动态分配空间并复制字符串内容。析构函数`string::~string()`用于释放字符串所占用的内存。`size_t string::size()const`函数返回字符串的长度。`const char* string::c_str()const`函数返回指向字符串的C风格字符数组的指针。
`char& string::operator[](size_t pos)`和`const char& string::operator[](size_t pos)const`函数实现了通过下标访问字符串中特定位置字符的功能。函数内部使用断言`assert`来确保访问的位置在有效范围内。
通过使用这些函数,可以方便地创建、访问和操作字符串对象。
2.3 reserve、push_back、append以及运算符+=重载
string.h:void reserve(size_t n);void push_back(char ch);void append(const char* str);string& operator+=(char ch);string& operator+=(const char*str);string.cpp: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 == _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;_str[_size + 1] = '\0';_size++;}void string::append(const char* str)//尾插字符串{size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}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;}
在上述代码中定义了一些用于修改字符串对象的函数。
在`string.h`中,`void reserve(size_t n)`函数用于保留至少能容纳n个字符的空间。`void push_back(char ch)`函数在字符串的末尾插入一个字符。`void append(const char* str)`函数在字符串的末尾插入一个C风格的字符数组。`string& operator+=(char ch)`函数用于在字符串末尾添加一个字符,并返回修改后的字符串对象的引用。`string& operator+=(const char* str)`函数用于在字符串末尾添加一个C风格的字符数组,并返回修改后的字符串对象的引用。
在`string.cpp`中,`void string::reserve(size_t n)`函数用于在需要的情况下扩展字符串的容量。如果n大于当前容量,它将创建一个新的更大的字符数组,并将原字符串的内容复制到新的数组中。然后释放原来的字符数组,并将指针指向新的数组,同时更新容量变量。`void string::push_back(char ch)`函数在字符串的末尾插入一个字符。如果字符串当前的大小已经等于容量,则先扩展容量,然后插入字符。`void string::append(const char* str)`函数在字符串的末尾插入一个C风格的字符数组。如果插入后的长度超过容量,则先扩展容量,然后将字符数组的内容复制到字符串中。`string& string::operator+=(char ch)`函数利用`push_back()`函数在字符串末尾添加一个字符,并返回修改后的字符串对象的引用。`string& string::operator+=(const char* str)`函数利用`append()`函数将C风格的字符数组添加到字符串末尾,并返回修改后的字符串对象的引用。
通过使用这些函数,可以方便地修改字符串对象,包括扩展容量、在末尾插入字符和字符数组等操作。
2.4 insert、erase以及find
string.h:void insert(size_t pos, char ch);void insert(size_t pos, const char*str);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); string.cpp:void string::insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}//memmove(_str + pos + 1, _str + pos, sizeof(char) * (_size - pos + 1));//法一size_t end = _size+1;//法二while (end > pos){_str[end] = _str[end-1];end--;}_str[pos] = ch;_size++;}void string::insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size+len > _capacity){reserve(_size+len);}//memmove(_str + pos + len, _str + pos, sizeof(char) * (_size - pos + 1));//法一size_t end = _size+len;//法二while (end >pos+len-1){_str[end] = _str[end-len];end--;}memcpy(_str + pos, str, len);_size+=len;}void string::erase(size_t pos, size_t len){assert(pos < _size);if (len >= _size-pos){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_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){char* p = strstr(_str + pos, str);return p - _str;}
在上述代码中定义了一些用于在字符串对象中查找和修改字符的函数。
在`string.h`中,`void insert(size_t pos, char ch)`函数在指定位置插入一个字符。`void insert(size_t pos, const char* str)`函数在指定位置插入一个C风格的字符数组。`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)`函数从指定位置开始,查找一个C风格的字符数组在字符串中第一次出现的位置。
在`string.cpp`中,`void string::insert(size_t pos, char ch)`函数在指定位置插入一个字符。如果字符串的大小已经等于容量,则先扩展容量,然后将插入位置后的字符依次后移,并将指定位置处的字符替换为插入的字符。`void string::insert(size_t pos, const char* str)`函数在指定位置插入一个C风格的字符数组。如果插入后的长度超过容量,则先扩展容量,然后将插入位置后的字符依次后移,并将指定位置处的字符替换为插入的字符数组中的字符。`void string::erase(size_t pos, size_t len)`函数从指定位置开始,删除指定长度的字符。如果删除的长度大于等于从指定位置到字符串末尾的长度,则将指定位置处的字符设为'\0',并更新字符串的大小。否则,将删除位置后的字符依次前移,覆盖被删除的字符,并更新字符串的大小。`size_t string::find(char ch, size_t pos)`函数从指定位置开始,在字符串中查找字符第一次出现的位置。遍历字符串中从指定位置开始的字符,如果找到与目标字符相同的字符,则返回该位置的索引。如果没有找到,返回`npos`。`size_t string::find(const char* str, size_t pos)`函数从指定位置开始,在字符串中查找一个C风格的字符数组第一次出现的位置。利用`strstr()`函数找到指定字符数组在字符串中的地址,并计算地址和字符串的地址差值,即为指定字符数组第一次出现的位置的索引。
通过使用这些函数,可以方便地在字符串对象中插入、删除和查找字符和字符数组。
2.5 swap、substr以及运算符< > <= >= == !=的重载
string.h:void swap(string& s);string substr(size_t pos = 0, 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;string.cpp:void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}string string::substr(size_t pos, size_t len){// len大于后面剩余字符,有多少取多少if (len > _size - pos){string sub(_str + pos);return sub;}else{string sub;sub.reserve(len);for (size_t i = 0; i < len; i++){sub += _str[pos + i];}return sub;}}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 *this < s || *this == s;}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);}
在上述代码中实现了一些额外的功能和操作符重载。
- `void swap(string& s)`:交换当前字符串和参数字符串的内容、大小和容量。
- `string substr(size_t pos = 0, 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`:重载不等于操作符,判断当前字符串是否不等于参数字符串。
这些功能和操作符重载使得字符串类更加方便实用,可以更灵活地进行字符串的操作和比较。
2.6 clear以及运算符<< >>重载
string.h:void clear();istream& operator>> (istream& is, string& str);ostream& operator<< (ostream& os, const string& str);string.cpp:void string::clear(){_str[0] = '\0';_size = 0;}istream& operator>> (istream& is, string& str){str.clear();char ch = is.get();while (ch != ' ' && ch != '\n'){str += ch;ch = is.get();}return is;}ostream& operator<< (ostream& os, const string& str){for (size_t i = 0; i < str.size(); i++){os << str[i];}return os;}
在上述代码中定义了一个简单的字符串类 string,并在 string 类中实现了 clear() 方法。
在 string 类的实现文件 string.cpp 中,clear() 方法将字符串数组 _str 的第一个字符设置为 '\0',并将字符串的大小 _size 设置为 0。
另外,还重载了输入流运算符 >> 和输出流运算符 <<。
在输入流运算符的实现中,先调用 string 类的 clear() 方法清空字符串,然后使用 istream 对象的 get() 方法逐个读取字符,直到遇到空格或换行符,将字符添加到字符串中。
在输出流运算符的实现中,使用 ostream 对象的 << 运算符逐个输出字符串中的字符。
最后,返回相应的输入流或输出流对象。
到此我们只是简单的模拟实现了一下STL中string的相关接口~,后续我们会一一展开学习的,希望这篇博客能给您带来一些启发和思考!那我们下次再一起探险喽,欢迎在评论区进行讨论~~~
相关文章:
探索C嘎嘎的奇妙世界:第十四关---STL(string的模拟实现)
1. string类的模拟实现 1.1 经典的string类问题 上一关已经对string类进行了简单的介绍,大家只要能够正常使用即可。在面试中,面试官总喜欢让学生自己来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数…...
【JavaScript脚本宇宙】玩转图像处理:从基础到高级,这些库你不能错过!
让你的网页图像栩栩如生:六种必备图像处理库 前言 在数字图像处理中,我们经常需要对图片进行各种操作,如调整亮度、对比度、饱和度等,以达到所需的效果。为了简化这些操作并提供更丰富的功能,出现了许多专门用于图像…...
python+unity手势控制地球大小
效果图如下 具体操作如下 1 在unity窗口添加一个球体 2 给球体添加材质,材质图片使用地球图片 地球图片如下 unity材质设置截图如下 3 编写地球控制脚本 using System.Collections; using System.Collections.Generic; using UnityEngine;public class test : MonoBehavio…...
CSS【实战】抽屉动画
效果预览 技术要点 实现思路 元素固定布局(fixed)在窗口最右侧外部js 定时器改变元素的 right 属性,控制元素移入,移出 过渡动画 transition transition: 过渡的属性 过渡的持续时间 过渡时间函数 延迟时间此处改变的是 right …...
【Linux Vim的保姆级教程】
🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共…...
力扣668.乘法表中第k小的数
力扣668.乘法表中第k小的数 二分查找 是否有k个比mid小的数 class Solution {public:int findKthNumber(int m, int n, int k) {auto check [&](int mid) -> bool{int res0;int row 1,col n;while(row < m){if(row * col < mid){res col;if(res > k) re…...
css伪类和伪元素选择器
伪类选择器关注元素的状态和条件,而伪元素选择器则关注元素的视觉表现和扩展。两者都是CSS中强大的工具,能够帮助开发者实现复杂的样式布局和交互效果。 伪类选择器 伪类选择器在CSS中用于选择元素的特定状态或位置。以下是一些常见的伪类选择器及其使…...
第壹章第15节 C#和TS语言对比-泛型
C#提供了泛型的完整支持,不仅在编译时,运行时仍然保留泛型的类型信息,同时提供了更加丰富的泛型约束和更加全面的协变逆变支持。TS的泛型,在语法表现形式上,和C#差不多,但本质上两者是不一样的。TS的泛型&a…...
苹果电脑下载vite包错
苹果电脑下载vite包错/Users/lili/.npm/_cacache/index-v5/c5/50/b451703d03b3802b9ee6b7ff2b0bde4de7f26830eb52c904d6911c137cf8包错解决方式 解决方式:sudo chown -R 501:20 "/Users/wangxin/.npm"...
自动化测试git的使用
git是一款分布式的配置管理工具。本文主要讲git如何在自动化测试中安装,上传及拉取下载代码。 1 、git 介绍 每天早上到公司,从公司的git服务器上下载最新的代码,白天在最新的代码基础上,编写新的代码,下班时把“代码…...
MyBatis系列四: 动态SQL
动态SQL语句-更复杂的查询业务需求 官方文档基本介绍案例演示if标签应用实例where标签应用实例choose/when/otherwise应用实例foreach标签应用实例trim标签应用实例[使用较少]set标签应用实例[重点]课后练习 上一讲, 我们学习的是 MyBatis系列三: 原生的API与配置文件详解 现在…...
Jenkins构建 Maven项目(微服务)并自动发布
前面讲了docker 安装Jenkins和gitlab代码管理工具,接下来我们讲一下Jenkins怎么构建 Maven项目。 1. 首先Jenkins配置下面3中工具类 首先是在本地安装三个jenkins自动配置相关的工具 1.1 JDK 由于我们使用docker来启动jenkins,其自带有jdk,…...
简单易用的多功能图床Picsur
什么是 Picsur ? Picsur 是一款易于使用、可自行托管的图片分享服务,类似于 Imgur,并内置转换功能。支持多种格式的图片,包括 QOI、JPG、PNG、WEBP(支持动画)、TIFF、BMP、GIF(支持动画…...
数据库-查询语句习题
SELECT Sname 姓 名,year of birth: 出生年,YEAR(GETDATE())-Sage BIRTHYEAR,LOWER(SNAME) SNAME --起别名 没有特殊字符不需要引号,有特殊字符要加引号;别名(解释作用显示给用户看)用空格或as连接 FROM STUDENT; --消除重复行 DI…...
进程间通信以及线程的同步互斥机制
1.进程间通信机制 常用的六种通信机制: 管道、消息队列、共享内存、信号灯集、信号、Socket 管道(Pipe)和无名管道(匿名管道): 管道是一种半双工的通信方式,数据只能单向流动,通常…...
优思学院|做车企的质量工程师转行跳槽能干嘛?
前言 质量工程师,是现代制造业和服务业中不可或缺的重要角色。他们负责制定和执行提高产品质量和优化业务流程的战略。这不仅涉及设立质量标准、开发测试系统,还包括记录生产过程中的问题并找到解决方案。尤其在汽车行业,由于对质量的高度要…...
ctfshow-web入门-命令执行(web53-web55)
目录 1、web53 2、web54 3、web55 1、web53 这里的代码有点不一样,说一下这两种的区别: (1)直接执行 system($c); system($c);这种方式会直接执行命令 $c 并将命令的输出直接发送到标准输出(通常是浏览器ÿ…...
【INTEL(ALTERA)】make: nios2-swexample-create:未找到命令
目录 说明 解决方法 说明 由于外部内存接口英特尔 Stratix 10 FPGA IP 出现问题,如果在 Windows 平台上使用英特尔 Quartus Prime Pro Edition Software v20.4 或更早版本的"使用软Nios处理器进行片上调试"选项,编译Nios II 片上处理器调试…...
一周刷爆leetcode!(b站视频)
文章目录 一、排序思想的题目二、使用步骤1. 一、排序思想的题目 跟着b站一周刷爆leetcode这个视频开始刷一下leetcode的题目 进行一下记录啥的 二、使用步骤 1. 315. 计算右侧小于当前元素的个数 代码如下: 写了一下暴力解法,没有通过 使用归并排序…...
1.xshell传不了文件输出0000如何解决.....2.k8s中metalLB文件内容
xshell传不了文件输出0000如何解决 centos版本 1,因为没有工具下载即可 yum -y install lrzszk8s中metalLB文件内容 2.metalLB文件内容 cat metallb-native.yaml apiVersion: v1 kind: Namespace metadata:labels:pod-security.kubernetes.io/audit: privilegedpod-securit…...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
分布式光纤声振传感技术原理与瑞利散射机制解析
分布式光纤传感技术(Distributed Fiber Optic Sensing,简称DFOS)作为近年来迅速发展的新型感知手段,已广泛应用于边界安防、油气管道监测、结构健康诊断、地震探测等领域。其子类技术——分布式光纤声振传感(Distribut…...
论文笔记:Large Language Models for Next Point-of-Interest Recommendation
SIGIR 2024 1 intro 传统的基于数值的POI推荐方法在处理上下文信息时存在两个主要限制 需要将异构的LBSN数据转换为数字,这可能导致上下文信息的固有含义丢失仅依赖于统计和人为设计来理解上下文信息,缺乏对上下文信息提供的语义概念的理解 ——>使用…...
spring中的@KafkaListener 注解详解
KafkaListener 是 Spring Kafka 提供的一个核心注解,用于标记一个方法作为 Kafka 消息的消费者。下面是对该注解的详细解析: 基本用法 KafkaListener(topics "myTopic", groupId "myGroup") public void listen(String message)…...
