string的使用和模拟实现| 细致到strcpy ,strstr,strcmp都不放过

我们模拟实现一个string 不是为了造一个比库里面更好的,而是熟悉语法,学习底层原理,复习数据结构等。

string的成员变量

string 就是有\0的顺序表,所以和顺序表的成员一样有,size,capacity,和一个指针

  1. 因为string的底层是字符串数组
    所以我们需要一个char*的指针指向数组
  2. size记录存放字符的个数不包括/0
  3. capacity作用是反应现在的容量以便后续扩容

string的构造方法

常用的就是第一个和第四个所以我们模拟实现也是这两个

在这里插入图片描述

用法

std::string s1; // 无参
std::string s2("hello word"); //字符串构造

无参的构造方法的实现

在这里插入图片描述

这样初始化可以吗?不可以 因为用空指针初始化_str则_str无法解引用,打印_str数组时会空指针异常。
怎么解决这个问题呢?可以给str开一个空间放/0

 string():_str(new char[1]),_size(0),_capacity(0){_str[0] = '/0';}

我们如果想传一个c字符串来构造字符串怎么做呢?

全缺省的构造参数的实现

就是用c字符串str的长度给_str开空间,再把str拷贝_str

 string(const char* str=""):_size(strlen(str)){_capacity = _size;_str = new char[_size + 1];strcpy(_str, str);
}

可能有的同学已经忘了strcpy的原理了我简单复习下

strcpy的模拟实现

就是挨个拷贝,连同反斜杠/0一起,因为没有/0就不能称为一个c串了。

char* my_strcpy(char* dest, const char* src) 
{assert(dest != nullptr && src != nullptr);char* ret = dest;while (*src != '\0') {*(dest++ ) = *(src++);}*dest = *src;return ret;
}

为什么这里的_size要+1?

因为strlen算出的字符串大小不包括/0,所以开辟的空间的大小要_size+1

为什么这里是默认传空字符串?

如果传空指针解strlen(str)没有/0作为字符串结束标志则会崩溃,不知道在哪结束所以不能传空指针,而空字符串默认是有一个/0的。

赋值运算符重载

 void  operator=(const string& s1)
{_size = s1._size;_capacity = s1._capacity;char* tmp = new char[_capacity + 1];strcpy(tmp, s1._str);delete[]_str;_str = tmp;
}                     

析构函数

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

遍历字符串

operator[]

这个有个非常进步的地方,就是它会越界检查了,c字符串越界的读不会检查而越界的写是抽查。

使用

s1[2]  // 非常的方便

传统c语言字符串下标遍历的缺点

越界读不报错
在这里插入图片描述
越界写报错但是是抽查的,在边界检查严格些

模拟实现

 char& operator[](size_t pos) {assert(pos< _size);return _str[pos];}

有了断言这样不管是读还是写都会检查了

迭代器

使用

 string s2("hello word");string::iterator it;it = s2.begin();while (it != s2.end()) {cout << *it<<" ";it++;}

注意这个end是\0的那个位置,而不是最后一个字符,因为是最后一个字符的话 it!=s2.end()就不打印最后一个字符了

不同类的迭代器可能不一样,我们用指针简单的实现一个string的迭代器

typedef  char* iterator;
///
iterator begin() 
{return _str;
}
iterator end() 
{return _str + _size;
}

范围for

当我们把迭代器写好后,范围for自动就成了。
因为范围for底层就是调用迭代器
在这里插入图片描述

成员函数

reserve

在这里插入图片描述
功能是给字符串的容量增长到n个字符的空间,当n<_capacity时不起作用。

reserve的使用

reserve(100);

reserve的模拟实现

void reserve(int n) 
{if (_capacity < n) {char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}
}

push_back

在这里插入图片描述
功能追加一个字符给字符串

push_back的使用

string s1("hello word");
s1.push_back('a');

push_back的模拟实现

 void push_back(char ch) {if (_size >= _capacity) {reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_str[_size+1] = '\0';_size++;}

append

appen的功能是把字符串变长通过在现存的字符串的末尾后面添加额外的字符串。
!

apeend的模拟实现

 void append(const string& s) {int len = strlen(s._str);if (_size > _capacity - len){reserve(_size + len);}strcpy(_str + _size, s._str);}

insert

在这里插入图片描述
功能是在pos位置之前插入其他字符/字符串

insert的使用

 std::string s("hello");std::string b(" word");s.insert(0, b);cout << s;

insert的模拟实现

插入字符
 void insert(size_t pos,char ch) {assert(pos <= _size);if (_size == _capacity) {reserve(_capacity ==0 ? 4:_capacity *2);}size_t end = _size+1;while (end > pos) {_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size++;}

版本二

void insert(size_t pos, char c) 
{assert(pos <= _size);if (_size >= _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}int end = _size;while (end >= (int)pos) //不强转pos end变成无符号数 一直>=0 {_str[end+1] = _str[end];end--;}_str[pos] = c;_size++;}

注意这里的pos一定要强制类型转换不然end会整型提升 当头插时pos = 0 end被提升为无符号数了 一直大于pos 0 所以会出错

插入字符串

 void  insert(size_t pos, string& s) {int len = s._size;assert(pos <= _size);if (_size+len  >= _capacity){reserve(_size + len);}size_t end = _size + len;while (end >= pos + len){_str[end] = _str[end - len];end--;}strncpy(_str + pos, s.c_string(),len);_size += len;}

注意这里要有strncp目的是不让\0拷贝下来

erase

功能:擦除pos位置起len长度的字符串
在这里插入图片描述
第一个用得很多我们模拟实现第一个

erase的使用

   std::string s1("hello word");s1.erase(12);

erase的模拟实现

首先判断pos位置是否合法 pose小于size才行 因为等于size就把\0擦除了。
判断 npos = -1 || len + pos > size 成功就全部擦除把o位置的值赋值成\0
或者把pos位置赋值成\0
再把 pos+len的位置拷贝到 pos处
size-= len

 void erase(size_t pos, size_t len = npos) {assert(pos < _size);if (len == npos || pos > _size - len) {_str[pos] = '\0';_size = pos;}else{strcpy(_str +pos, _str + len+pos);_size -= len;}}

resize

功能是改变字符串的长度
如果当前的size小于n,就缩短到n
若n大于size,如果指定了字符就在后面插入指定的字符否则插入\0以达到n的长度
在这里插入图片描述

resize的使用

  std::string s1("hello word");s1.resize(100,'a');cout << s1;

rise的模拟实现

首先判断size和n的关系
n小于size 则把n位置的字符赋值\0
否则 扩容到n的长度
把下标size到下标n-1的字符全部赋值成ch
size 改为 n

void resize(size_t n, char ch ='\0')
{if (n <= _size) {_str[n] = '/0';_size = n;}else {reserve(n);for (size_t i = _size; i < n; i++) {_str[i] = ch;}_str[n] = '\0';_size = n;}
}

substr

在这里插入图片描述

功能:返回从主串截取的从pos位置长度为len的子串

substr的使用

std::string s1("hello word");
cout << s1.substr(0, 5);

substr的模拟实现

因为要返回一个新的字符串,所以我们先定义个新串
然后一个+=循环 ,循环条件从pos到下标pos+len-1

string substr(size_t pos = 0, size_t len = npos)
{string sub;//if (len == npos || len >= _size-pos)if (len >= _size - pos){for (size_t i = pos; i < _size; i++){sub += _str[i];}}else{for (size_t i = pos; i < pos + len; i++){sub += _str[i];}}return sub;

find

寻找子串在主从中从pos位置开始第一个出现的位置
或者找一个字符在字符串pos后的第一个位置
没有找到则返回npos

使用

     string s1("hello word");int ret1 = s1.find("word", 3);int ret2 = s1.find("w",3);

模拟实现

size_t find(const string& str, size_t pos = 0) const 
{assert(pos < _size);char * ret= strstr(_str + pos, str.c_string());if(ret)return ret - _str;return npos;
}size_t find(char ch, size_t pos) 
{assert(pos < _size);for (size_t i = pos; i < _size; i++) {if (_str[i] == ch) {return i;}}return npos;
}

这里我们直接用的库函数strstr 可能有的同学忘了ststr的功能
我简单复习一下 就是在主串中在子串第一个出现的位置然后返回匹配到的第一个位置的指针。没有找到则返回空指针

strstr的模拟实现

这里我们使用暴力查找的方法简单的模拟一下

     char* my_strstr(const char* str, const char* substr) {int len = strlen(substr);int i = 0;while (i < strlen(str)) {size_t j = 0;for (; j < len; j++){if (substr[j] == str[i]){i++;}else{break;}}if (j == len) {return (char*) str+i - len;}i++;}return nullptr;}

swap

交换两个字符串

使用

string s1("hello word");
string s2;
s2.swap(s1);

改写

在这里插入图片描述
我们直接把T变为 string string c(a); a =b ;b = c;
这里会走一次拷贝构造 两次赋值运算符重载 总共三次构造 还外加一次析构 c 代价太大了 所以必须重写

  void swap(string s1){std::swap(_size, s1._size);std::swap(_capacity, s1._capacity);std::swap(_str, s1._str);}

为了防止别人调用库里面的swap(T a,T b)我们需要在类外面写一个相同参数的swap,这样调用的时候就优先调用我们写了的。
因为函数模板有现成的吃现成的。

	void swap(string& x, string& y){x.swap(y);}

string的赋值运算符重载

==

    bool operator==(const string& s1, const string& s2) {return strcmp(s1.c_string(), s2.c_string()) ==0;}

可能有的同学忘了strcmp的原理
就是两个字符串从头开始比较 相等就继续走 如果遇到串1的第一个字符大于串2的第一个字符 return 1 小于则return -1 全部走完了则return 0

strcmp的模拟实现

    int my_strcmp(const char* a, const char* b) {while (*a == *b && *a != '\0' && *b != '\0'){a++;b++;}if (*a > *b)return 1;if (*a < *b)return -1;return 0;}

<

bool operator<(const string& s1, const string& s2){int ret = strcmp(s1.c_str(), s2.c_str());return ret < 0;}

>>

这里不能用 scanf 和 cin 因为 cin 和scanf 都把空格和 回车作为分隔符 读不到 则程序永远不会结束。

istream& operator>>(istream& in,  string& s) 
{char ch;s.clear();ch = in.get();while (ch != ' ' && ch != '\n') {s += ch;ch = in.get();}return in;
}

改进

我们这个+=需要频繁扩容不太好。
如果我们用reserve 开个很大的空间 则输入很小的时候又浪费了
所以我们用一个数组暂存字符 然后根据数组的大小一次性开辟好空间,就不用频繁扩容了。

 istream& operator>>(istream& in,  string& s) {char ch;s.clear();char buff[128];ch = in.get();int i = 0;while (ch != ' ' && ch != '\n') {//s += ch;buff[i++] = ch;if (i == 127) {buff[127] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0) {buff[i] = '\0';s += buff;}return in;}

什么叫现代写法呢?就是假借他人的手完成相应的功能

拷贝构造函数的现代写法

在这里插入图片描述
借构造函数的手 构造一个和s1一样的字符串 然后和 this 交换

         /* string(const string &s1) {_size = s1._size;_capacity = s1._capacity;char* tmp = new char[_size + 1];strcpy(tmp, s1._str);this->_str = tmp;}*/string(const string& s1) {string tmp(s1.c_string());swap(tmp);}

赋值运算符重载的现代写法

第三个版本是由第二个版本而进过来的
第二个版本借助 s 拷贝构造 字符串ss
第三个版本 我们没有用引用编译器帮我们调用了拷贝构造

// version 1/*string& operator=(const string& s){string ss(s);swap(ss);return *this;}*/// version 2/*string& operator=(const string& s){string ss(s);swap(ss);return *this;}*/// version 3string& operator=( string ss){swap(ss);return *this;}

谢谢观看

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

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

相关文章

【Linux系统编程】进程程序替换

介绍&#xff1a; 进程程序替换是指将一个进程中正在运行的程序替换为另一个全新的程序的过程&#xff0c;但替换不是创建新进程&#xff0c;只是将对应程序的代码和数据进行替换。具体来说&#xff0c;这个替换过程涉及将磁盘中的新程序加载到内存结构中&#xff0c;并重新建立…

防火墙常用功能配置

防火墙&#xff1a;为了限制不同区域之间的流量通信。默认有一条拒绝所有的策略。 现在的防火墙主要作用&#xff1a;是区域隔离和访问控制。 安全防护是核心特性 路由器&#xff1a;ACL列表&#xff0c;控制流量 入侵防御&#xff1a;网络攻击 文件过滤&#xff0c;内容过滤&…

电脑里的图片杂乱无章怎么办?使用汇帮批量重命名一键帮你解决 大量图片如何批量重命名?

当我们面临大量的图片需要重命名时&#xff0c;这无疑是一项繁琐而耗时的任务。然而&#xff0c;通过一些有效的方法和工具&#xff0c;我们可以使这个过程变得更加高效和轻松。以下介绍的这款汇帮批量重命名软件&#xff0c;能够帮助您有效地重命名大量的图片。 想要快速的进…

C++容器适配器与stack,queue,priority_queue(优先级队列)的实现以及仿函数(函数对象)与deque的简单介绍

&#x1f389;个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名乐于分享在学习道路上收获的大二在校生 &#x1f648;个人主页&#x1f389;&#xff1a;GOTXX &#x1f43c;个人WeChat&#xff1a;ILXOXVJE &#x1f43c;本文由GOTXX原创&#xff0c;首发CSDN&…

【玩转AI绘画】有奖活动火热进行中,参与赢取耳机键盘等好礼!

AI 绘画发展迅猛&#xff0c;各种创新插件如 animatediff、instantid、controlnet 、roop 等遍地开花&#xff0c;极大地激发了 StableDiffusion 的应用潜力。AI 绘画的使用场景不断扩展&#xff0c;如 AI 视频制作、Q 版头像生成、老照片修复、照片高清化等。腾讯云高性能应用…

【OceanBase诊断调优】—— 敏捷诊断工具obdiag一键分析OB集群日志设计与实践

最近总结一些诊断OCeanBase的一些经验&#xff0c;出一个【OceanBase诊断调优】专题&#xff0c;也欢迎大家贡献自己的诊断OceanBase的方法。 1. 前言 obdiag定位为OceanBase敏捷诊断工具。1.2版本的obdiag支持诊断信息的一键收集&#xff0c;光有收集信息的能力&#xff0c;…

【nnUNetv2实践】一、nnUNetv2安装

nnUNet是一个自适应的深度学习框架&#xff0c;专为医学图像分割任务设计。以下是关于nnUNet的详细解释和特点&#xff1a; 自适应框架&#xff1a;nnUNet能够根据具体的医学图像分割任务自动调整模型结构、训练参数等&#xff0c;从而避免了繁琐的手工调参过程。自动化流程&am…

提升地理空间分析效率,火山引擎ByteHouse上线GIS能力

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;回复【1】进入官方交流群 在数字化时代&#xff0c;地理空间分析&#xff08;Geospatial Analytics&#xff09;成为辅助企业市场策略洞察的重要手段。无论是广告投放的精准定位&#xff0c;…

Leetcode 1. 两数之和

心路历程&#xff1a; 很简单的题&#xff0c;双层暴力就可以&#xff0c;用双指针的话快一点。暴力时间复杂度O( n 2 n^2 n2)&#xff0c;双指针时间复杂度O(nlogn) O(n) O(n) O(nlogn)。 注意的点&#xff1a; 1、题目需要返回原数组的索引&#xff0c;所以排序后还需要…

豆瓣电影信息爬取与可视化分析

目录 一、项目背景 二、代码 三、总结 一、项目背景 &#xff08;1&#xff09;利用requests库采集豆瓣网分类排行榜 (“https://movie.douban.com/chart”)中各分类类别前100部电影的相关信息并存储为csv文件。 &#xff08;2&#xff09;利用获取的13个分类类别共1300部电…

Linux:搭建ntp服务器

我准备两个centos7服务器 一个为主服务器连接着外网&#xff0c;并且搭建了ntp服务给其他主机同步 另外一个没有连接外网&#xff0c;通过第一台设备去同步时间 首先两个服务器都要安装ntp软件 yum -y install ntp 再把他俩的时间都改成别的 左侧的是主服务器&#xff0c;主…

idea项目mapper.xml中的SQL语句黄色下划线去除

问题描述 当我们使用idea开发java项目时&#xff0c;经常会与数据库打交道&#xff0c;一般在使用mybatis的时候需要写一大堆的mapper.xml以及SQL语句&#xff0c;每当写完SQL语句的时候总是有黄色下划线&#xff0c;看着很不舒服。 解决方案&#xff1a; 修改idea的配置 Edi…

服务器段的连接端口和监听端口编程实现

new ServerSocket(int)是开启监听端口&#xff0c;并不是连接端口。真正的连接端口是随机开辟的空闲端口&#xff0c;当连接创建完成后&#xff0c;监听关口可以继续等待下一次连接请求&#xff0c;处于空闲等待状态。 编程实现方式 1 、主线程一直处于阻塞等待状态&#xff0c…

C 练习实例77-指向指针的指针-二维数组

关于数组的一些操作 #include<stdio.h> #include<stdio.h> void fun(int b[],int length) {for(int i0;i<length;i){printf("%d ",b[i]);}printf("\n");for(int i0;i<length;i){ //数组作为形参传递&#xff0c;传递的是指针&#xff0…

人工智能如何撬动新质生产力发展?

全国两会期间&#xff0c;“新质生产力”成为高频词&#xff0c;引发高度关注。新质生产力是由技术革命性突破、生产要素创新性配置、产业深度转型升级催生的当代先进生产力。而人工智能被视为形成新质生产力的重要引擎。 随着人工智能&#xff08;AI&#xff09;技术跨越奇点…

Python3+RIDE+RobotFramework自动化测试框架搭建

Python2.7已于2020年1月1日开始停用&#xff0c;之前RF做自动化都是基于Python2的版本。 没办法&#xff0c;跟随时代的脚步&#xff0c;我们也不得不升级以应用新的控件与功能。 升级麻烦&#xff0c;直接全新安装。 一、Python安装 最新版Python下载地址&#xff1a;http…

Python环境下基于注意力机制的小样本轴承故障诊断

传统的基于特征提取与分类相结合的轴承智能诊断算法&#xff0c;对信号处理要求很高的专家经验&#xff0c;既费时又缺乏通用性。基于深度学习的智能轴承故障诊断方由于具有强大的特征提取能力&#xff0c;避免了繁琐复杂的特征提取工作&#xff0c;但是大多数研究都是在标准数…

共谋企业出海新篇章纷享销客荣获数字中国企业峰会“卓越成果奖”

3月9日&#xff0c;2024数字中国企业峰会在杭州西湖中维香溢大酒店成功举办&#xff0c;众多数字化领域专家、知名企业 CIO 代表到场。峰会旨在推动数字化转型与创新发展&#xff0c;为企业出海和国际合作搭建交流与合作的平台。本次峰会的颁奖环节&#xff0c;纷享销客凭借其卓…

【已解决】MySQL:常用的除法运算+精度处理+除数为0处理

目录 问题现象&#xff1a; 问题分析&#xff1a; 拓展&#xff1a; 1、除法运算&#xff1a; 拓展&#xff1a;MySQL中常用的几种除法运算 1、取整除法 2、浮点数除法 3、取余除法 4、向上取整除法 5、向下取整除法 2、运算结果的精度处理 1.1、浮点数 1.2、总位数 1.3、…

android 怎么自定义view

首先了解view的绘制流程: 所以onmeasure ---测量view onlayout---确定view大小----》所以继承ViewGroup必须要重写onlayout,确定子view 而onDraw----是继承view时候需要操作的。 所以:自定义ViewGroup一般是利用现有的组件根据特定的布局方式来组成新的组件。 自定义Vi…