当前位置: 首页 > 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…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...

学习一下用鸿蒙​​DevEco Studio HarmonyOS5实现百度地图

在鸿蒙&#xff08;HarmonyOS5&#xff09;中集成百度地图&#xff0c;可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API&#xff0c;可以构建跨设备的定位、导航和地图展示功能。 ​​1. 鸿蒙环境准备​​ ​​开发工具​​&#xff1a;下载安装 ​​De…...

上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式

简介 在我的 QT/C 开发工作中&#xff0c;合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式&#xff1a;工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...

macOS 终端智能代理检测

&#x1f9e0; 终端智能代理检测&#xff1a;自动判断是否需要设置代理访问 GitHub 在开发中&#xff0c;使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新&#xff0c;例如&#xff1a; fatal: unable to access https://github.com/ohmyzsh/oh…...