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 在程序运行过程中,经常会遇到各种各样的错误,这些错误统称为“异常”。这些异常有的是由于开发者将关键字敲错导致的…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...
Golang——7、包与接口详解
包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...
