当前位置: 首页 > news >正文

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是表示字符串的字符串类&#xff0c;对C语言的字符串指针进行了包装。 2. 该类的接口与常规容器的接口基本相同&#xff0c;有增删查改等&#xff0c;再添加了一些专门用来操作string的常规操作。 二.成员变量 创建string类的时候要在自己的命名空间…...

怎么做好企业短信服务呢?(文字短信XML接口示例)

企业短信服务已经成为各行各业都信赖的行业推广方式之一&#xff0c;并且短信行业也与时俱进的发展着&#xff0c;随之而来的就是市场上短信平台的数量也随之增多。那么怎么在鱼龙混杂的短信行业中选择适合自己的企业短信服务平台呢&#xff1f;企业短信服务平台又适用于哪些应…...

鸿蒙小案例-音乐播放器

之前参加鸿蒙比赛的音乐播放器 效果展示 HF音乐效果展示 功能列 有一些功能没写上去&#xff0c;自行发掘 说明&#xff1a; 1.API:网易云接口&#xff0c;QQ个人接口&#xff0c; 需要请看gitee 2.本地关系型数据由bug,提的工单已确认&#xff0c;建议使用API11,12,9的不稳…...

语言模型测试系列【9】

语言模型 文心一言讯飞星火通义千问2.5豆包360智脑百小应腾讯元宝KimiC知道 好长时间没有做语言模型的测试了&#xff0c;一方面是没有好的素材&#xff0c;各模型都在升级优化&#xff0c;而且频率很高&#xff1b;另一方面近期在阅读和学习其他的知识&#xff0c;所以更的也…...

优思学院|质量工程师工资不高怎么办?

你是否曾经好奇&#xff0c;为什么在职场中&#xff0c;质量工程师的工资普遍不高&#xff1f;这一现象背后的原因&#xff0c;实际上与他们的职业门槛和专业知识密切相关。早期&#xff0c;国内的质量工程师入行门槛较低&#xff0c;许多人即使没有任何专业知识也可以进入这一…...

【面向就业的Liux基础】从入门到熟练,探索Linux的秘密(一)

主要帮助大家面向工作过程中Linux系统常用的命令联系&#xff0c;采用极致的实用主义&#xff0c;帮助大家节省时间。 文章目录 前言 一、linux系统 二、linux系统基本命令 1.Linux系统的目录结构 2. 常用命令介绍 3.命令演示 4.作业练习 总结 前言 主要帮助大家面向工作过程中…...

高效数据处理的前沿:【C++】、【Redis】、【人工智能】与【大数据】的深度整合

目录 1.为什么选择 C 和 Redis&#xff1f; 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 硬件加速领域&#xff0c;Vitis HLS 提供了强大的抽象并行编程模型。这些模型包括控制驱动和数据驱动的任务级并行性&#xff08;TLP&#xff09;&…...

VUE3 学习笔记(12):对比Vuex与Pinia状态管理的基本理解

在组件传值中&#xff0c;当嵌套关系越来越复杂的时候必然会将混乱&#xff0c;是否可以把一些值存在一个公共位置&#xff0c;无须传值直接调用呢&#xff1f;VUEX应运而生&#xff0c;但是从VUE3开始对VUEX的支持就不那么高了&#xff0c;官方推荐使用Pinia。 Vuex配置 ST1:…...

区间预测 | Matlab实现QRCNN-BiGRU-Attention分位数回归卷积双向门控循环单元注意力机制时序区间预测

区间预测 | Matlab实现QRCNN-BiGRU-Attention分位数回归卷积双向门控循环单元注意力机制时序区间预测 目录 区间预测 | Matlab实现QRCNN-BiGRU-Attention分位数回归卷积双向门控循环单元注意力机制时序区间预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实…...

TypeScript算法每日一题:赎金信(383)

作者&#xff1a;前端小王hs 阿里云社区博客专家/清华大学出版社签约作者✍/CSDN百万访问博主/B站千粉前端up主 题库&#xff1a;力扣 题目序号&#xff1a;383&#xff08;简单&#xff09; 题目&#xff1a;赎金信 给你两个字符串ransomNote 和 magazine&#xff0c;判断ran…...

springboot 作为客户端接收服务端的 tcp 长连接数据,并实现自定义结束符,解决 粘包 半包 问题

博主最近的项目对接了部分硬件设备&#xff0c;其中有的设备只支持tcp长连接方式传输数据&#xff0c;博主项目系统平台作为客户端发起tcp请求到设备&#xff0c;设备接收到请求后作为服务端保持连接并持续发送数据到系统平台。 1.依赖引入 连接使用了netty&#xff0c;如果项…...

kuka编程怎么加中文:解锁KUKA机器人编程中的中文支持

kuka编程怎么加中文&#xff1a;解锁KUKA机器人编程中的中文支持 在工业自动化领域&#xff0c;KUKA机器人以其卓越的性能和广泛的应用而备受赞誉。然而&#xff0c;对于许多中国用户来说&#xff0c;如何在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个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有4个页面。 二、作品演示 三、代…...

Client ID 与Client Secret

什么是 Client ID 和 Client Secret&#xff1f; 在现代应用程序中&#xff0c;特别是在涉及到OAuth 2.0身份验证和授权时&#xff0c;Client ID 和 Client Secret是非常重要的概念。它们通常用于验证和授权第三方应用程序&#xff0c;以便安全地访问受保护的资源或API。 Cli…...

React中实现大模型的打字机效果

React 想实现一个打字机的效果&#xff0c;类似千问、Kimi 返回的效果。调用大模型时&#xff0c;模型的回答通常是流式输出的&#xff0c;如果等到模型所有的回答全部完成之后再展示给最终用户&#xff0c;交互效果不好&#xff0c;因为模型计算推理时间比较长。本文将采用原生…...

十二、配置注解执行SQL

简化一下流程&#xff0c;主要可以分为下面几步&#xff1a; 1.解析配置&#xff0c;写入配置项 2.执行SQL 3.封装结果 通过注解配置SQL主要体现在解析部分&#xff0c;这部分要分别做解析XML还是配置注解的接口&#xff0c;拿到sql以后&#xff0c;select的处理和insert/upda…...

阿里云计算之运维概念学习笔记(一)

运维管理 运维管理&#xff08;Operation and Maintenance Management, 简称O&M管理&#xff09;是指通过科学的管理方法和技术手段&#xff0c;对IT系统和基础设施进行监控、维护、优化和保障&#xff0c;以确保系统的高可用性、稳定性、安全性和性能。运维管理涵盖了硬件…...

异常概述

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在程序运行过程中&#xff0c;经常会遇到各种各样的错误&#xff0c;这些错误统称为“异常”。这些异常有的是由于开发者将关键字敲错导致的&#xf…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

智能AI电话机器人系统的识别能力现状与发展水平

一、引言 随着人工智能技术的飞速发展&#xff0c;AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术&#xff0c;在客户服务、营销推广、信息查询等领域发挥着越来越重要…...