库中是如何实现string类的?

在这里插入图片描述

🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻推荐专栏1: 🍔🍟🌯C语言初阶
🐻推荐专栏2: 🍔🍟🌯C语言进阶
🔑个人信条: 🌵知行合一
🍉本篇简介:>:讲解如何模拟实现C++中的string类.
金句分享:
✨你要做多大的事情,就要承受多大的压力!✨

前言

我们先认识一下string类的框架.

class string{public://成员函数private:char* _str;                 //字符串指针 size_t _capacity;           //容量size_t _size;               //当前字符有效个数}:

在这里插入图片描述

框架图:
在这里插入图片描述

目录

  • 前言
  • 一、构造函数与析构函数
    • (1) 无参构造:
    • (2) 使用常量字符串构造
    • (3) 拷贝构造
    • (4) 析构函数
  • 二、capacity相关操作
    • (1) reserve函数
    • (2) resize函数
    • (3) empty函数
    • (4) size和capacity函数
  • 三、访问与遍历
    • (1) 迭代器
    • (2)下标访问符 方括号`[ ]`重载
  • 四、修改与查找
    • (1) push_back函数
    • (2) append函数
    • (3) find函数
    • (4) insert函数
    • (5) erase函数
  • 五、运算符重载
    • (1) 流运算符重载
      • 流插入运算符
      • 流提取运算符
    • (2) 比较运算符重载

一、构造函数与析构函数

在这里插入图片描述

(1) 无参构造:

我们可以试着看一下库里面是如何赋值的?

	std::string s1;cout << "s1= " << s1 << endl;

在这里插入图片描述
所以,对于无参构造,我们只需要将*str赋值为空串就行了.

注意:
""(中间没有空格)

(2) 使用常量字符串构造

  1. 先计算字符串的长度.
  2. 将长度值赋值给_size_capacity .
  3. 申请一块为_capacity+1大小的空间.(+1是为了存储'\0')
  4. 将字符串中的值按字节拷贝至string类中的_str.

代码实现:

//全缺省构造函数,,默认初始化为空字符
string(const char* str = "")//无参也是调用这个 {_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];//里面其实有一个字符'\0'// strcpy(_str, str);              //遇到'\0'结束拷贝,也会吧'\0'拷贝过去memcpy(_str, str, _size + 1);}

(3) 拷贝构造

这里注意实现深拷贝即可.

string(const string& s)       //注意这里+const  普通对象可以调用,const对象也可以调用
{_size = s._size;_capacity = s._capacity;_str = new char[_capacity + 1];//需要多一个字节存放,字符'\0'memcpy(_str,s._str,s._size+1);      
}

(4) 析构函数

析构函数注意释放动态申请的空间即可.

~string(){_size = 0;_capacity = 0;delete[]_str;_str = nullptr;}

二、capacity相关操作

(1) reserve函数

reserve():请求改变容量的大小,类似于扩容从操作.

  1. 向堆区申请一块n+1大小的新空间.
  2. 将旧空间的数据拷贝到新空间,
  3. 释放旧空间
  4. _str指向新空间
  5. 更新容量capacity
void reserve(size_t n)
{if (n > _capacity){//申请一块新空间char* tmp = new char[n + 1];memcpy(tmp, _str, _size + 1);//不建议使用strcpy,可能存在中间有'\0'的强开delete[] _str;//释放旧空间_str = tmp;_capacity = n;}
}

(2) resize函数

resize():用于改变字符串的有效字符长度.不够的地方用第二个参数填充.

在这里插入图片描述

步骤:

  1. 如果n<size(即n<当前长度):
    ①直接在n位置处赋值为’\0’.
    _size更新为n

  2. 如果n>=size:
    ①调用扩容函数,扩容至n大小.
    ②超出部分用字符'c'填充
    ③更新_size,并在最后一个位置设置为’\0’

代码实现:

  void resize(size_t n, char c = '\0'){if (n < _size){_str[n] = '\0';_size = n;}else{reserve(n);//无论需不要扩容,不需要扩容时reserve内部会什么也不做,需要扩容时,reserve会扩容.memset(_str + _size, c, n - _size);_size = n;_str[_size] = '\0';}}

(3) empty函数

如果_size值为0,则为空,返回true.
否则返回false;

  bool empty()const{//size=0则为空,返回truereturn _size == 0 ? true : false;}

(4) size和capacity函数

这两个函数,直接返回值即可.

	size_t size()const{return _size;}size_t capacity()const{return _capacity;}

三、访问与遍历

(1) 迭代器

迭代器的介绍

C++迭代器是一个用于遍历容器(如vector、list、set等)中的元素的对象。迭代器的作用类似于指针,可以通过解引用操作符(*)获取容器中的元素值,也可以通过自增操作符(++)移动迭代器指向下一个元素。迭代器可以访问容器中的元素,也可以修改容器中的元素值。

在这里插入图片描述

注意迭代器的定义,迭代器是左闭右开的区间.

public:typedef char* iterator;typedef const char* const_iterator;//普通迭代器iterator begin(){return _str;             //返回第一个字符位置}iterator end(){return _str + _size;     //返回最后一个有效字符的下一个位置}//常类迭代器const const_iterator begin()const{return _str;             //返回第一个字符位置}const const_iterator end()const{return _str + _size;     //返回最后一个有效字符的下一个位置}

(2)下标访问符 方括号[ ]重载

返回_str中第index的位置

 char& operator[](size_t index){//判断位置是否合法assert(index >= 0 && index < _size);return *(_str + index);}const char& operator[](size_t index)const{assert(index >= 0 && index < _size);return *(_str + index);}

四、修改与查找

(1) push_back函数

push_back尾部插入一个字符

在进行插入操作之前,要先考虑扩容的情况.

需要注意的是,如果采用无参构造,刚开始容量是0.
这就导致是初次扩容,容量开始是0,所以这里要判断扩容前,容量是否是0,再考虑1.5倍或者二倍扩容.

void push_back(char c)
{if (_size + 1 > _capacity){//如果capacity是0,则无法进行1.5倍扩容//reserve(_capacity * 1.5);reserve(_capacity == 0 ? 4 : _capacity * 1.5);//扩容多少没有标准,2倍或者1.5倍扩容都可,}_str[_size] = c;_size++;        //有效数据+1_str[_size] = '\0';
}

(2) append函数

append尾部追加字符串

 void append(const char* str){int sz=strlen(str);//如果容量不够,就申请新空间if (_size + sz > _capacity){reserve(_capacity +sz);}//追加新的字符串memcpy(_str + _size, str, sz);_size += sz;_str[_size] = '\0';}

(3) find函数

string中查找目标字符,通过遍历比较.
第二个参数表示从pos位置开始查找.

顺序查找即可

size_t find(char c, size_t pos = 0) const
{assert(pos < _size);for (int i = pos; i < _size; i++){if (_str[i] == c){return i;}}return npos;
}

字符串匹配:查找string类的中的目标字串

字符串匹配算法,这里简化,直接调用库函数strstr,就不手撕算法了.

 // 返回子串s在string中第一次出现的位置size_t find(const char* s, size_t pos = 0) const{assert(pos < _size);const char* ptr = strstr(_str + pos, s);//通过调用库函数strstr找到字符串出现的位置的指针if (ptr){return ptr - _str;}else{return npos;}}

(4) insert函数

pos位置插入一个字符:
老规矩,先扩容,学过数据结构的小伙伴应该知道,需要先移动数据再进行插入数据操作.

顺序表任意位置插入

// 在pos位置上插入字符c/字符串str,并返回该字符的位置string& insert(size_t pos, char c)
{assert(pos >= 0 && pos<= _size);if (_size + 1 > _capacity){reserve(_capacity * 1.5);}int i = 0;//移动数据for (i = _size; i > pos - 1; i--){_str[i] = _str[i - 1];}_str[pos - 1] = c;_size++;return *this;
}

pos位置插入一段字符串:

这里在移动数据时,注意0号位置插入时移动.
如果我们只是将前面的数据直接往后移动字符串长度大小的位置,则到插入0号位置时,前面的数据是非法的,此处设计时,需要注意.

string& insert(size_t pos, const char* str)
{assert(pos >= 0 && pos <= _size);int sz = strlen(str);//如果容量不够,就申请新空间if (_size + sz > _capacity){reserve(_capacity + sz);}int i = 0;//移动数据,这里需要注意0号位置插入时是否移动数据非法.size_t end = _size;while (end >= pos && end != npos){_str[end + sz] = _str[end];--end;}//插入字符串for (i = pos; i <pos+sz; i++){_str[i] = str[i-pos];}_size+=sz;return *this;
}

(5) erase函数

erase:删除从pos位置开始往后len长度的元素,并返回删除后的string.

在这里插入图片描述

在这里插入图片描述

 // 删除pos位置上的元素,并返回该元素的下一个位置string& erase(size_t pos, size_t len=npos){assert(pos <= _size);if (len == npos || pos + len >= _size)//如果要求删除的长度+pos超过了string中有效字符的长度{_size = pos;_str[_size] = '\0';}else{size_t end = pos + len;while (end <= _size){_str[pos++] = _str[end++];}_size -= len;}return *this;}

五、运算符重载

(1) 流运算符重载

采用友元函数的方式,实现流提取与流插入运算符重载.

流插入运算符

 ostream& operator<<(ostream& _cout, const cjn::string& s)//记得包在cjn命名空间里面{//在实现了迭代器的情况下,可以使用范围forfor (auto& in : s)      //依次取出string类中的全部字符,插入进流{_cout << in;}return _cout;   //返回输出流}

流提取运算符

 istream& operator>>(istream& in, string& s){s.clear();//如果s中本身有数据,先将有效数据清除char ch = in.get();//处理缓冲区的空格和换行,因为可能有人先输入了空格或者换行导致读取数据失败while (ch == ' ' || ch == '\n'){ch = in.get();}//有效数据插入进schar buff[128];//为了避免从小的容量一次次扩容int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;//将读取到的数据先存放进临时数组if (i == 127)//只有等数据满127时,才将数据插入进s{buff[i] = '\0';s += buff;i = 0;}ch = in.get();//继续获取有效数据}if (i != 0)//最后,如果buff数组中还有数据,则将这些剩余数据插入{buff[i] = '\0';s += buff;}return in;}

(2) 比较运算符重载

两个字符串比较,我们利用memcmp函数比较两字符串中较短字符串的长度位数.
然后根据memcmp返回值进行进一步判断.

 bool operator<(const string& s){int length = s._size;//谁短,length就等于谁if (_size < length)   length = _size;int ret=memcmp(_str, s._str, length);if (ret == 0)//如果短的 与长的前半部分相等{if (_size< s._size)//比比较的字符串短,则<成立,true{return true;}return false;}if(ret==-1)//表示右操作数大,<满足return true;if (ret == 1)//表示左操作数大,<不满足return false;}

三目运算符写起来可能不大好理解,但是代码看起来很简洁

 bool operator<(const string& s) const{int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);//按短的进行比较//如果ret==0,则比较长度,s长则返回真,否则返回假//ret!=0则-1表示真,1表示假return ret == 0 ? _size < s._size : ret < 0;                      }

其他运算符直接复用或者比较简单,这里不做解释.

bool operator<=(const string& s){return *this < s || *this == s;}bool operator>(const string& s){return !(*this <= s);}bool operator>=(const string& s){return !(*this < s);}bool operator==(const string& s){if (memcmp(_str, s._str, _size) == 0){if (_size == s._size){return true;}return false;}}bool operator!=(const string& s){return !(*this == s);}

博主能力有限,无法严格按照库中的方法实现,比如采用内存池等技术,还有部分函数并未实现,模拟实现string的目的只是为了我们更好的理解string类,而不是真正让我们去写一个库函数.
拜拜了.
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/122475.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计及电池储能寿命损耗的微电网经济调度(matlab代码)

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序参考文献《考虑寿命损耗的微网电池储能容量优化配置》模型&#xff0c;以购售电成本、燃料成本和储能寿命损耗成本三者之和为目标函数&#xff0c;创新考虑储能寿命损耗约束、放电深度约束和储能循环次…

PyQt5报错Process finished with exit code -1073740791 (0xC0000409)

点击按钮之后&#xff0c;就直接退出程序&#xff0c;控制台出现一个提示&#xff1a;解决办法&#xff1a; 在PyCharm中打开Run菜单&#xff0c;找到Edit Configurations进入&#xff0c;勾选Emulate terminal in output console即可。 然后再运行一下程序&#xff0c;就可以…

【管理运筹学】第 7 章 | 图与网络分析(1,图论背景以及基本概念、术语、矩阵表示)

文章目录 引言一、图与网络的基本知识1.1 图与网络的基本概念1.1.1 图的定义1.1.2 图中相关术语1.1.3 一些特殊图类1.1.4 图的运算 1.2 图的矩阵表示1.2.1 邻接矩阵1.2.2 可达矩阵1.2.3 关联矩阵1.2.4 权矩阵 写在最后 引言 按照正常进度应该学习动态规划了&#xff0c;但我想…

爬虫到底难在哪里?

目录 爬虫到底难在哪里 怎么学习爬虫 注意事项 爬虫工具 总结 学习Python爬虫的难易程度因人而异&#xff0c;对于具备编程基础的人来说&#xff0c;学习Python爬虫并不困难。Python语言本身比较简单易学&#xff0c;适合初学者使用。 爬虫到底难在哪里 爬虫的难点主要包…

阿里云2核4G服务器5M带宽五年租用价格表

阿里云2核4G服务器5M带宽可以选择轻量应用服务器或云服务器ECS&#xff0c;轻量2核4G4M带宽服务器297元一年&#xff0c;2核4G云服务器ECS可以选择计算型c7、c6或通用算力型u1实例等&#xff0c;买5年可以享受3折优惠&#xff0c;阿腾云分享阿里云服务器2核4G5M带宽五年费用表&…

[git] 如何克隆仓库,进行项目撰写,并绑定自己的远程仓库

摘要&#xff1a;删除.git文件&#xff0c;才可重新绑定远程仓库。 具体步骤&#xff1a; 文件夹右键&#xff0c;进入”Git Bash Here“执行命令 1. 执行 ”git clone 仓库地址“&#xff0c;克隆仓库 2. 在生成的仓库中&#xff0c;删除 .git 文件 3. git init 初始化仓库…

时序预测 | MATLAB实现LSSVM最小二乘支持向量机时间序列预测未来

时序预测 | MATLAB实现LSSVM最小二乘支持向量机时间序列预测未来 目录 时序预测 | MATLAB实现LSSVM最小二乘支持向量机时间序列预测未来预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现LSSVM时间序列预测未来(最小二乘支持向量机)&#xff1b; 2.运行环境Mat…

SQLI-labs-第五关

知识点&#xff1a;布尔盲注 思路&#xff1a; 1、判断注入点 首先&#xff0c;我们看看正常的回显内容 ?id1 接着输入?id1 &#xff0c;结果出现语句错误 这里说明存在单引号的闭合错误 ?id1 and 11-- ?id1 and 12-- 这里没有任何回显信息&#xff0c;可以准确的确…

Ansible之playbook剧本

一、playbook概述1.1 playbook 介绍1.2 playbook 组成部分 二、playbook 示例2.1 playbook 启动及检测2.2 实例一2.3 vars 定义、引用变量2.4 指定远程主机sudo切换用户2.5 when条件判断2.6 迭代2.7 Templates 模块1.先准备一个以 .j2 为后缀的 template 模板文件&#xff0c;设…

RDMA 相关bug记录

对于 Client 来讲&#xff0c;setupConnection 中的 cm_id 应该是本地的&#xff0c;意味着后续 create pd \ cq \ qp 等等传入的 cm_id 都是本地 id。但是对于 Server 来讲&#xff0c;收到 client 的链接请求时将 client 的 cm_id 传入 setupConnection&#xff0c;意味着后续…

JavaScript-----对象(创建对象、数组与字符串)

目录 前言&#xff1a; 1. JavaScript创建对象 1.1 对象的创建 1.2 对象的调用 1.3 for-in循环语句 2.内置对象 2.1 Array&#xff08;数组&#xff09;对象 属性和方法 2.2 String&#xff08;字符串&#xff09;对象 属性和方法 2.3 Math对象 2.4 日期对象 前言&a…

机器人中的数值优化(六)—— 线搜索最速下降法

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

LeetCode 18 四数之和

题目链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目解析 固定两个数&#xff0c;然后利用双指针来进行剩下两个数的筛选 主要使用的是三数之和的思想&#xff0c;具体可以看我上篇博客 注意去重 代码 class Solution { public:vector<…

STM32微控制器的低功耗模式

STM32微控制器的低功耗模式(Low-power modes):Sleep mode、Stop mode 和 Standby mode。 1.1 Sleep Mode(睡眠模式): 把STM32微控制器当作一位劳累的工人,他在工作过程中需要短暂的休息。在Sleep模式下,微控制器会关闭一部分电路,减小功耗,但仍然保持对中央处理单…

机器人中的数值优化(十二)——带约束优化问题简介、LP线性规划

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

【FPGA项目】沙盘演练——基础版报文收发

​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ 第1个虚拟项目 前言 点灯开启了我们的FPGA之路&#xff0c;那么我们来继续沙盘演练。 用一个虚拟项目&#xff0c;来入门练习&#xff0c;以此步入数字逻辑的大门。 Key Words&…

功能测试常用的测试用例大全

登录、添加、删除、查询模块是我们经常遇到的&#xff0c;这些模块的测试点该如何考虑 1)登录 ① 用户名和密码都符合要求(格式上的要求) ② 用户名和密码都不符合要求(格式上的要求) ③ 用户名符合要求&#xff0c;密码不符合要求(格式上的要求) ④ 密码符合要求&#xff0c;…

python-爬虫-xpath方法-批量爬取王者皮肤图片

import requests from lxml import etree获取NBA成员信息 # 发送的地址 url https://nba.hupu.com/stats/players # UA 伪装 google header {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.3…

自然语言处理历史史诗:NLP的范式演变与Python全实现

目录 一、引言什么是自然语言处理&#xff1f;语言与人类思维自然语言的复杂性NLP的历史轨迹 二、20世纪50年代末到60年代的初创期符号学派重要的研究和突破 随机学派重要的研究和突破 三、20世纪70年代到80年代的理性主义时代基于逻辑的范式重要的研究和突破 基于规则的范式重…