探索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…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
