C++ -- string常用接口的底层实现
一.string介绍
1. string是表示字符串的字符串类,对C语言的字符串指针进行了包装。
2. 该类的接口与常规容器的接口基本相同,有增删查改等,再添加了一些专门用来操作string的常规操作。
二.成员变量
创建string类的时候要在自己的命名空间内,不然会和std的string重名了。函数都写在这个空间里,我的取名为qf,你们可以自己取喜欢的名字。
namespace qf
{class string{private:char* _str;int _size;//有效字符个数int _capacity;//有效空间 \0不算有效空间const static size_t npos;};const size_t string::npos = -1;//初始化
}
npos 会初始化成 -1,转化成size_t的话会变成2^32 - 1;很大很大,所以一般npos用于表示无穷;
三.构造函数,析构函数
string(const char* str = "")
{//开辟空间,小于4个就给4的空间。int len = strlen(str) ;if (len < 4){_capacity = 4;}else{_capacity = len;}_size = len;// '\0'不算有效字符,所以要多整一个空间_str = new char[_capacity + 1];strcpy(_str, str);
}//拷贝构造
string(string& s)//记得加上引用,不然一直会进入拷贝死循环。
{int size = s.size();if (size < 4){_capacity = 4;}else{_capacity = size;}_size = size;//'\0'不算有效字符,所以要多整一个空间_str = new char[_capacity + 1];strcpy(_str, s.c_str());
}//析构函数
~string()
{//如果_str是空那就不用delete;if (_str){delete[] _str;}_size = 0;_capacity = 0;
}
有些朋友可能会感到奇怪,因为编译器其实会自动实现一个拷贝构造,但系统实现的拷贝构造内容完全一样,char* _str 这个指针储存的地址都是一样的。例如 string a(b);a由b拷贝来的,改变a的字符串,b的字符串也会变。我们要让它们独立。所以我们要重新开一份相同大小的空间再去拷贝,这就是深浅拷贝的区别。系统自动生成的拷贝构造是浅拷贝,我们上面写的是深拷贝。
四.简单的接口
//返回字符串长度
size_t size()const
{return _size;
}//返回c语言的字符串
char* c_str()const
{return _str;
}//返回容量
size_t capacity()const
{return _capacity;
}
//判断是否为空
bool empty()const
{return 0 == _size;
}
五.尾上插入字符与字符串
//重新分配capacity大小
void reserve(size_t capacity)
{_capacity = capacity;//多开一个空间是因为为了放'\0'的char* tem = new char[_capacity + 1];strcpy(tem, _str);delete[] _str;_str = tem;
}//插入单个字符
void push_back(char ch)
{//检查容量是否满了if (_size == _capacity){reserve(2 * _capacity);}_str[_size] = ch;_size++;_str[_size] = '\0';
}
//插入字符串
void append(const char* str)
{int len = strlen(str);//检查容量是否足够if (_capacity < len + _size){reserve(len + _size);//在里面会扩容且改变capacity的值}strcpy(_str + _size, str);//将传来的字符串加到原来的字符串后面_size += len;
}
六.指定插入与删除
//在pos位置加上字符串,pos后面的字符串向后移
string& inserter(size_t pos, const char *str)
{assert(str);if (pos > _size)return *this;else{int len = strlen(str);//检测容量是否足够if (_size + len > _capacity)reserve(_size + len);//挪数据,//如果pos = 0,那么i 要等于-1才能结束,// size_t 和int 比较会自动转换成容量大的,所以// -1 > 0 会死循环,所以要将pos强制类型转化成intfor (int i = _size - 1; i >= (int)pos; i--){_str[i + len] = _str[i];}//将字符串拷贝到pos后面for (int i = 0; i < len; i++){_str[pos] = str[i];pos++;}_size += len;_str[_size] = '\0';}
}//在pos位置删n个字符。如果只传pos说明后面都要删光光
string& erase(size_t pos ,size_t n = npos)
{// 1.pos >= size 那就不删直接返回if (pos >= _size);// 2.n + pos > size 那就pos后面可以删完else if (n + pos >= _size){_size = pos;_size = '\0';}// 3.pos + n < size 那就pos + n后面的元素移到pos的位置else{for (int i = pos + n; i < _size; i++){_str[pos] = _str[i];pos++;}_size -= n;_str[_size] = '\0';}return *this;
}//截取n个字符然后后面加给定的字符
//有三种情况
//1. n < size
//2. size < n < capacity;
//3. n < capacity 第二和第三一样,加个扩容就行
string& resize(size_t n, char ch = '\0')
{//1.if (n < _size){_size = n;_str[_size] = '\0';return *this;}//2 and 3if (n > _capacity){reserve(n);}for (int i = _size; i < n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';return *this;
}
七.运算符重载
//要重载俩
//这个返回的是可以改的,
//比如 string s("abcd"); s[0] +=1;
//s内容就变成了"bbcd";
char& operator[](size_t i)
{return _str[i];
}//这个返回的是不可改的,
//因为用上面的权限会被放大。
const char& operator[](size_t i)const
{return _str[i];
}string& operator+= (const char ch)
{push_back(ch);return *this;
}string& operator+= (const char* str)
{append(str);return *this;
}bool operator> (const string& s)const
{int ret = strcmp(_str, s.c_str());return ret > 0;
}bool operator== (const string& s)const
{int ret = strcmp(_str, s.c_str());return ret == 0;
}bool operator>= (const string& s)const
{//直接复用上面return *this > s || *this == s;
}bool operator< (const string& s)const
{return !(*this >= s);
}bool operator<= (const string& s)const
{return !(*this > s);
}
八.查找字符和字符串
//注意点就是要返回npos(2^32 - 1),然后利用接口strstr
//返回npos说明没找到
size_t find(const char* str)const
{char* p = strstr(_str, str);if (p == nullptr)return npos;elsereturn p - _str;//俩个指针相减刚好是这个的角标}size_t find(char ch)const
{for (int i = 0; i < _size; i++){if (_str[i] == ch){return i;}}return npos;
}
九.输入输出重载
ps:1.这个要放在命名空间外面,返回以后还能输出其他的内容所以要重载std::ostream和std::istream。
2.不能放类里面, << 的左操作数一定要是std::ostream,第一个参数默认左操作数。
std::ostream& operator<<(std::ostream& out, const string& s){for (int i = 0; i < s.size(); i++){out << s[i];}return out;}std::istream& operator>>(std::istream& in, string& s){while (1){//这里不能用cout << ch;//因为cout << ch;读到' ' 和 '\n'直接跳过了;char ch = in.get();if (ch == ' ' || ch == '\n')break;elses += ch;}return in;}
}
十.迭代器的实现
C++中迭代器和指针很像,为什么要有迭代器呢? 当然是因为方便操作,C++中有很多容器,遍历方式各有不同,为了让他们一样,就产生了迭代器,string类的迭代器就是指针。但其他类可能是一些更复杂的自定义类型。
typedef char* iterator;const typedef char* const_iterator;const_iterator begin()const{return _str;}const_iterator end()const{return _str + _size;}iterator begin(){return _str;}iterator end(){return _str + _size;}
迭代器的使用如下
qf::string::iterator it = s.begin();while (it != s.end()){cout << *it << ' ';it++;}cout << endl;
范围for就是利用了迭代器如下代码编译器会转化成上面的。
for (auto& e : s){cout << e << ' ';}cout << endl;
十一.拷贝构造和operator=优化
void swap(string& s)
{//因为在qf命名空间内,所以调用swap要说明是std的std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}//现代写法
string(const string& s):_str(nullptr)
{string tem(s._str);swap(tem);
}string& operator=(string s)
{swap(s);return *this;
}
很简洁对不对。利用了临时变量出了作用域会自动销毁,并且swap交换时会帮你new好空间。极其方便。
十二.总代码
#include<iostream>
#include<assert.h>
namespace qf
{class string{public://迭代器typedef char* iterator;const typedef char* const_iterator;const_iterator begin()const{return _str;}const_iterator end()const{return _str + _size;}iterator begin(){return _str;}iterator end(){return _str + _size;}//构造函数string(const char* str = ""){//开辟空间int size = strlen(str) ;if (size < 4){_capacity = 4;}else{_capacity = size;}_size = size;//'\0'不算有效字符,所以要多整一个空间_str = new char[_capacity + 1];strcpy(_str, str);}string(char ch){_str = new char[2];_size = 1;_capacity = 1;_str[0] = ch;}//古代写法//string(string& s)//{// int size = s.size();// if (size < 4)// {// _capacity = 4;// }// else// {// _capacity = size;// }// _size = size;// //'\0'不算有效字符,所以要多整一个空间// _str = new char[_capacity + 1];// strcpy(_str, s.c_str());//}void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}//现代写法string(const string& s):_str(nullptr){string tem(s._str);swap(tem);}string& operator=(string s){swap(s);return *this;}char* c_str()const{return _str;}//析构函数~string(){if (_str){delete[] _str;}_size = 0;_capacity = 0;}size_t size()const{return _size;}size_t capacity()const{return _capacity;}//要重载俩,因为这个是可读可写char& operator[](size_t i){return _str[i];}//这个只能读,一般打印的时候用const char& operator[](size_t i)const{return _str[i];}bool empty()const{return 0 == _size;}void push_back(char ch){if (_size == _capacity){reserve(2 * _capacity);}_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char* str){int len = strlen(str);if (_capacity < len + _size){reserve(len + _size);}strcpy(_str + _size, str);_size += len;}string& operator+= (const char ch){push_back(ch);return *this;}bool operator> (const string& s)const{int ret = strcmp(_str, s.c_str());return ret > 0;}bool operator== (const string& s)const{int ret = strcmp(_str, s.c_str());return ret == 0;}bool operator>= (const string& s)const{//直接复用上面return *this > s || *this == s;}bool operator< (const string& s)const{return !(*this >= s);}bool operator<= (const string& s)const{return !(*this > s);}string& operator+= (const char* str){append(str);return *this;}//重新分配capacity大小void reserve(size_t capacity){_capacity = capacity;char* tem = new char[_capacity + 1];strcpy(tem, _str);delete[] _str;_str = tem;}//有三种情况。截取n个字符然后后面加给定的字符//1. n < size //2. size < n < capacity;//3. n < capacity 第二和第三一样加个扩容就行string& resize(size_t n, char ch = '\0'){//1.if (n < _size){_size = n;_str[_size] = '\0';return *this;}//2 and 3if (n > _capacity){reserve(n);}for (int i = _size; i < n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';return *this;}//在指定位删n个string& erase(size_t pos ,size_t n = npos){// 1.pos >= size 那就不删直接返回if (pos >= _size);// 2.n + pos > size 那就pos后面可以删完else if (n + pos >= _size){_size = pos;_size = '\0';}// 3.pos + n < size 那就pos + n后面的元素移到pos的位置else{for (int i = pos + n; i < _size; i++){_str[pos] = _str[i];pos++;}_size -= n;_str[_size] = '\0';}return *this;}//在指定位置插入string& inserter(size_t pos, const char *str){assert(str);if (pos > _size)return *this;else{int len = strlen(str);if (_size + len > _capacity)reserve(_size + len);//挪数据,如果pos = 0,那么i 要等于-1才能结束,// size_t 和int 比较会自动转换成容量大的,所以// -1 > 0 会死循环,所以要强制类型转化for (int i = _size - 1; i >= (int)pos; i--){_str[i + len] = _str[i];}// strncpy(_str + _size, str, len);for (int i = 0; i < len; i++){_str[pos] = str[i];pos++;}_size += len;_str[_size] = '\0';}}//注意点就是要返回npos,然后利用接口strstrsize_t find(const char* str)const{char* p = strstr(_str, str);if (p == nullptr)return npos;elsereturn p - _str;}size_t find(char ch)const{for (int i = 0; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}private:char* _str;int _size;//有效字符int _capacity;//有效空间 \0不算有效哦const static size_t npos;};const size_t string::npos = -1;std::ostream& operator<<(std::ostream& out, const string& s){for (int i = 0; i < s.size(); i++){out << s[i];}return out;}std::istream& operator>>(std::istream& in, string& s){while (1){char ch = in.get();if (ch == ' ' || ch == '\n')break;elses += ch;}return in;}
}
ps:仅供参考,有问题可以一起交流,加油,共勉!
相关文章:
C++ -- string常用接口的底层实现
一.string介绍 1. string是表示字符串的字符串类,对C语言的字符串指针进行了包装。 2. 该类的接口与常规容器的接口基本相同,有增删查改等,再添加了一些专门用来操作string的常规操作。 二.成员变量 创建string类的时候要在自己的命名空间…...
怎么做好企业短信服务呢?(文字短信XML接口示例)
企业短信服务已经成为各行各业都信赖的行业推广方式之一,并且短信行业也与时俱进的发展着,随之而来的就是市场上短信平台的数量也随之增多。那么怎么在鱼龙混杂的短信行业中选择适合自己的企业短信服务平台呢?企业短信服务平台又适用于哪些应…...
鸿蒙小案例-音乐播放器
之前参加鸿蒙比赛的音乐播放器 效果展示 HF音乐效果展示 功能列 有一些功能没写上去,自行发掘 说明: 1.API:网易云接口,QQ个人接口, 需要请看gitee 2.本地关系型数据由bug,提的工单已确认,建议使用API11,12,9的不稳…...
语言模型测试系列【9】
语言模型 文心一言讯飞星火通义千问2.5豆包360智脑百小应腾讯元宝KimiC知道 好长时间没有做语言模型的测试了,一方面是没有好的素材,各模型都在升级优化,而且频率很高;另一方面近期在阅读和学习其他的知识,所以更的也…...
优思学院|质量工程师工资不高怎么办?
你是否曾经好奇,为什么在职场中,质量工程师的工资普遍不高?这一现象背后的原因,实际上与他们的职业门槛和专业知识密切相关。早期,国内的质量工程师入行门槛较低,许多人即使没有任何专业知识也可以进入这一…...
【面向就业的Liux基础】从入门到熟练,探索Linux的秘密(一)
主要帮助大家面向工作过程中Linux系统常用的命令联系,采用极致的实用主义,帮助大家节省时间。 文章目录 前言 一、linux系统 二、linux系统基本命令 1.Linux系统的目录结构 2. 常用命令介绍 3.命令演示 4.作业练习 总结 前言 主要帮助大家面向工作过程中…...
高效数据处理的前沿:【C++】、【Redis】、【人工智能】与【大数据】的深度整合
目录 1.为什么选择 C 和 Redis? 2.人工智能与大数据的背景 1.大数据的挑战 2.人工智能的需求 3.C 与 Redis 的完美结合 1.安装 Redis 和 Redis C 客户端 2.连接 Redis 并进行数据操作 高级数据操作 列表操作 哈希操作 4.与大数据和人工智能结合 5.实际应…...
Vitis HLS 学习笔记--控制驱动与数据驱动混合编程
目录 1. 简介 2. 示例分析 2.1 代码分析 2.2 控制驱动TLP的关键特征 2.3 数据驱动TLP的关键特征 3. 总结 1. 简介 在 HLS 硬件加速领域,Vitis HLS 提供了强大的抽象并行编程模型。这些模型包括控制驱动和数据驱动的任务级并行性(TLP)&…...
VUE3 学习笔记(12):对比Vuex与Pinia状态管理的基本理解
在组件传值中,当嵌套关系越来越复杂的时候必然会将混乱,是否可以把一些值存在一个公共位置,无须传值直接调用呢?VUEX应运而生,但是从VUE3开始对VUEX的支持就不那么高了,官方推荐使用Pinia。 Vuex配置 ST1:…...
区间预测 | Matlab实现QRCNN-BiGRU-Attention分位数回归卷积双向门控循环单元注意力机制时序区间预测
区间预测 | Matlab实现QRCNN-BiGRU-Attention分位数回归卷积双向门控循环单元注意力机制时序区间预测 目录 区间预测 | Matlab实现QRCNN-BiGRU-Attention分位数回归卷积双向门控循环单元注意力机制时序区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实…...
TypeScript算法每日一题:赎金信(383)
作者:前端小王hs 阿里云社区博客专家/清华大学出版社签约作者✍/CSDN百万访问博主/B站千粉前端up主 题库:力扣 题目序号:383(简单) 题目:赎金信 给你两个字符串ransomNote 和 magazine,判断ran…...
springboot 作为客户端接收服务端的 tcp 长连接数据,并实现自定义结束符,解决 粘包 半包 问题
博主最近的项目对接了部分硬件设备,其中有的设备只支持tcp长连接方式传输数据,博主项目系统平台作为客户端发起tcp请求到设备,设备接收到请求后作为服务端保持连接并持续发送数据到系统平台。 1.依赖引入 连接使用了netty,如果项…...
kuka编程怎么加中文:解锁KUKA机器人编程中的中文支持
kuka编程怎么加中文:解锁KUKA机器人编程中的中文支持 在工业自动化领域,KUKA机器人以其卓越的性能和广泛的应用而备受赞誉。然而,对于许多中国用户来说,如何在KUKA编程中加入中文支持却成为了一个挑战。本文将从四个方面、五个方…...
hadoop集群中zookeeper的搭建与原理解释
搭建zookeeper 将zookeeper的apache-zookeeper-3.5.7-bin.tar.gz解压到/export/servers下 tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz -C /export/servers为了方便后期使用解压后的文件夹改名为zookeeper-3.5.7 mv apache-zookeeper-3.5.7-bin zookeeper-3.5.7先进入zoo_…...
HTML静态网页成品作业(HTML+CSS)—— 父亲节节日介绍网页(4个页面)
🎉不定期分享源码,关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 🏷️本套采用HTMLCSS,未使用Javacsript代码,共有4个页面。 二、作品演示 三、代…...
Client ID 与Client Secret
什么是 Client ID 和 Client Secret? 在现代应用程序中,特别是在涉及到OAuth 2.0身份验证和授权时,Client ID 和 Client Secret是非常重要的概念。它们通常用于验证和授权第三方应用程序,以便安全地访问受保护的资源或API。 Cli…...
React中实现大模型的打字机效果
React 想实现一个打字机的效果,类似千问、Kimi 返回的效果。调用大模型时,模型的回答通常是流式输出的,如果等到模型所有的回答全部完成之后再展示给最终用户,交互效果不好,因为模型计算推理时间比较长。本文将采用原生…...
十二、配置注解执行SQL
简化一下流程,主要可以分为下面几步: 1.解析配置,写入配置项 2.执行SQL 3.封装结果 通过注解配置SQL主要体现在解析部分,这部分要分别做解析XML还是配置注解的接口,拿到sql以后,select的处理和insert/upda…...
阿里云计算之运维概念学习笔记(一)
运维管理 运维管理(Operation and Maintenance Management, 简称O&M管理)是指通过科学的管理方法和技术手段,对IT系统和基础设施进行监控、维护、优化和保障,以确保系统的高可用性、稳定性、安全性和性能。运维管理涵盖了硬件…...
异常概述
自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在程序运行过程中,经常会遇到各种各样的错误,这些错误统称为“异常”。这些异常有的是由于开发者将关键字敲错导致的…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
