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

string接口模拟实现2

文章目录

    • 浅拷贝
    • insert插入一个字符
    • insert插入一个字符串
    • 删除erase
    • find(查找一个字符)
    • \\\\\\\find(查找一个字符串:子串substr)
    • 查找的练习(网址)
    • 赋值operator=
    • 比较大小
    • 流插入
    • 流提取

浅拷贝

	//浅拷贝string::string(const string& str){//开一样大的空间,一样大的值_str = new char[str._capacity+1];strcpy(_str, str._str);_size = str._size;_capacity = str._capacity;}

记得断言,pos<=_size

insert插入一个字符

//string.cpp中的内容void string::insert(size_t pos, char ch){assert(pos<_size);if (_size == _capacity){reserve(2 * _capacity);}size_t i = _size;for (i = _size; i >= pos; i--){_str[i+1] = _str[i]; //为什么是[i+1]=[i],而不是[i]=[i-1]呢?因为我们还需要将下标为_size的\0也往后挪一格,\0之后位置的下标是_size+1,也就是i+1}_str[pos] = ch;_size++;}

在上面案例的测试中,测试极端案例,比如头插会发生意外,当i=0的时候,我们已经将_str[1]=_str[0]了,此时i=0,若是再- -,i就会很大,因为i是unsigned int类型,所以0-1的结果是很大(绕环,成为很大的值)。

在这里插入图片描述



在这里插入图片描述
那i成为很大的值,那肯定大于pos,以至于无法出循环。

方法一:
想让i成为-1,那就让它的类型成为int。

int i = _size;

但是又有一个问题:i > pos,i是int类型,pos是size_t类型。有一个知识点:当一个操作符两边的操作数类型不一样的时候,我们可以将范围小的向范围大的强转(在这里:无符号向有符号转换)for (i = _size; i >= (int)pos; i--)

	void string::insert(size_t pos, char ch){assert(pos<_size);if (_size == _capacity){reserve(_capacity == 0?4: 2 * _capacity);}int i = _size;for (i = _size; i >= (int)pos; i--){_str[i+1] = _str[i];}_str[pos] = ch;_size++;}

方法二:
i初始化为_size+1

	void string::insert(size_t pos, char ch){assert(pos<_size);if (_size == _capacity){reserve(_capacity == 0?4: 2 * _capacity);}size_t i = _size+1;for (i = _size + 1; i > pos; i--){_str[i] = _str[i-1];}_str[pos] = ch;_size++;}

insert插入一个字符串

	void string::insert(size_t pos, const char* str){assert(pos<_size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);//这是新找了一个地方开空间}size_t i = _size+len;for (i = _size+ len; i >= pos+len; i--){_str[i] = _str[i-len];}size_t j = 0;for (j = 0; j < len; j++){_str[pos + j] = str[j];}}

这种方法也存在bug(越界),当我想在下标为0的位置插入(即头插一个字符串)

_str[i] = _str[i-len];

删除erase

可以画图自己理解

  • npos是类的静态成员变量。静态成员变量不可以直接给缺省值,需要在类外面。(因为在声明处给值,是传给了初始化列表,而静态成员变量不会在初始化列表里面)
    在这里插入图片描述
  • 当声明和定义分离时,不要将 size_t string::npos = -1;放在.h里面,不然.h会在string.cpptest.cpp都展开。那npos这个变量会有两份,那两个链接的时候合并生成可执行程序,它们的符号表里面npos就会合并到一起,那怎么会有两个npos呢,一个成员不能定义两份,所以:静态成员变量当生命和定义分离时,需要将 size_t string::npos = -1;放在.cpp
    在这里插入图片描述
  1. 如果删除的长度>len或者len没有传值,使用的缺省值npos,那么就是将pos以及之后的全部删掉。(即将pos的位置直接修改为‘\0’)
  2. 如果删除的长度<len,那么将len那一节去掉,再将pos+len之后的移过来。
void string::erase(size_t pos, size_t len)  //0 npos
{//如果len大于pos后面字符的个数if (_size - pos <= len || len == npos){_str[pos] = '\0';}else{size_t i = 0;size_t s = _size - pos-len;while (i<=s){_str[pos] = _str[pos + len];pos++;i++;}_size -= len;}
	void string::erase(size_t pos, size_t len)  //0 npos{//如果len大于pos后面字符的个数if (_size - pos <= len || len == npos){_str[pos] = '\0';}else{size_t end = pos + len;while (end <= _size){_str[end - len] = _str[end];++end;}_size -= len;}}

find(查找一个字符)

记得断言:pos<_size

size_t string::find(char c, size_t pos)
{assert(pos < _size);size_t i = pos;while (i < _size){if (_str[i] == c)return i;i++;}return npos;
}

\\\\find(查找一个字符串:子串substr)

  • 子串匹配即暴力匹配(strstr)
    在这里插入图片描述
	size_t string::find(const char* str, size_t pos){size_t len = strlen(str);assert(pos + len < _size);const char* p1 = strstr(_str, str);if (p1 == nullptr)return npos;else{//已知地址,如何获得它的下标//我们知道第一个元素的地址_str。p1-_str就是下标return p1 - _str;}}

这样也可以
在这里插入图片描述

查找的练习(网址)

在这里插入图片描述

void test6()
{//想取出来域名editor.csdn.nethou::string str1("https://editor.csdn.net/md?articleId=144002569");size_t m = str1.find(':') + 3;  //m是域名中e的下标size_t n = str1.find('/', m);   //n是从域名开始的下一个‘/’的下标if (m != hou::string::npos && n != hou::string::npos){hou::string domain = str1.substr(m, n - m);std::cout << domain.c_str() << std::endl;}
}

赋值operator=

这个两个操作数均已初始化过,都有各自的值,但是现在想让str2=str1.

在这里插入图片描述
让str2=str1,那么str2将有一部分空间被浪费。

在这里插入图片描述
让str2=str1,但是1比2大,装不下。

所以,第一步应该是将str2给释放掉,然后按照str1的大小来设置。不然如果之后再扩容的话,也是需要去找新的空间,然后将原来的释放掉。

string& string::operator=(const string& str)
{delete[]_str;_str = new char[str._capacity + 1];_size = str._size;_capacity = str._capacity;return *this;
}

上面写的有点不完善。如果是将自己赋值给自己,会成功吗?

当然不会。因为在刚开始,就已经将它自己给释放掉了。所以需要有条件。

在这里插入图片描述

比较大小

比较大小需要在全局实现。为什么呢?如果在类里面,则第一个参数就固定死了,一定是隐藏的this指针。而在全局的话,则第一个是左操作数,第二个是右操作数。

字符串如何比较呢?

是从头开始,一个字符一个字符的比较它们的ascll码值。

	bool operator==(const string& left, const string& right){return (strcmp(left.c_str(), right.c_str()) == 0);}bool operator!=(const string& left, const string& right){return !(right == left);}bool operator<(const string& left, const string& right){return strcmp(left.c_str(), right.c_str()) < 0;}bool operator>(const string& left, const string& right){return strcmp(left.c_str(), right.c_str()) > 0;}bool operator<=(const string& left, const string& right){return !(left > right);}bool operator>=(const string& left, const string& right){return !(left < right);}

在这里插入图片描述

流插入

必须是引用返回,且是在全局实现这个函数
在实现流插入之前,我们需要实现size()operator[],这两个在是流插入时需要使用。

还有一个注意点是: ostream& operator<< (ostream& os, string& str);第二个参数是const修饰的,所以不能修改。而str[i]是允许用户修改里面的内容的。因此,有两个解决办法:

(1.将const取消掉。
(2.实现一个函数const char& operator[]( size_t pos) const;.(最前面的const指的是返回值被const修饰,不能更改.最后的那个const的含义是:这个函数可以被const修饰的对象调用。比如,

hou::string str1("houhouohu");//str1这个对象是被const修饰的
std::cout<<str1[3]<<std::endl;

(3.把string的const迭代器实现出来(这个是为什么呢?看下图)。
在这里插入图片描述

//第一种方法	                      //cout<<str1;其中,cout是os,str1是strostream& operator<< (ostream& os,  string& str){for (size_t i = 0; i < str.size(); i++){os << str[i];}return os;}
//第二种const char& string::operator[](size_t pos)const{return _str[pos];}ostream& operator<< (ostream& os, const string& str){for (size_t i = 0; i < str.size(); i++){os << str[i];}return os;}

流提取

是将内容搬到str里面。(一个字符一个字符的提取)

在此之前要实现+=。实现+=就相当于是push_back一个字符。但push_back的时候需要判断空间是否足够,可以考虑实现reverse,也可以当场实现开空间。

还要注意一个问题就是:cin和scanf都有一个共同的特点,就是,自动忽略空格和换行(它们会认为空格和换行是用来分割的,然后它们就直接忽略它了,不提取)。

在这里插入图片描述
由上图可以看出,当str1里面有内容,而我又cin了,在此之后,原本的内容会被清除,所以我们需要用到清楚clear()

	void string::reverse(size_t n){if (n > _capacity){char* tmp = new char[n];strcpy(tmp, _str);delete[]_str;_str = tmp;_capacity = n;}}void string::push_back(char ch){if (_size == _capacity){reverse( _capacity == 0 ? 4 : 2 * _capacity);}_str[_size] = ch;_size++;}string& string::operator+=(const char ch){push_back(ch);return *this;}void string::clear(){//只是清楚内容_str[0] = '\0';_size = 0;}istream& operator>> (istream& is, string& str){char ch;//	is >> ch;ch=is.get();while (ch != ' ' && ch != '\0'){str += ch;//该扩容就扩容,该加等就加等,是+=的事情了//is >> ch;ch=is.get();}return is;}

http://www.mrgr.cn/news/78625.html

相关文章:

  • Vue 3 中实现页面特定功能控制
  • 排序算法2
  • nodejs建立TCP服务器端和TCP客户端之间的连接
  • 商君书的驭民之术
  • linux minio安装
  • 智截违规,稳保安全 | 聚铭视频专网违规外联治理系统新品正式发布
  • 18. C++STL 4(vector的使用, 空间增长, 迭代器失效详解)
  • HCIA笔记6--路由基础
  • 【真正离线安装】Adobe Flash Player 32.0.0.156 插件离线安装包下载(无需联网安装)
  • 透视投影(Perspective projection)与等距圆柱投影(Equirectangular projection)
  • GateWay使用手册
  • gcc编译
  • 如何在Spark中使用gbdt模型分布式预测
  • HTML飞舞的爱心(完整代码)
  • HarmonyOS Next 模拟器安装与探索
  • 十四(AJAX)、AJAX、axios、常用请求方法(GET POST...)、HTTP协议、接口文档、form-serialize
  • 基于vite创建一个脚手架(快速入门)
  • 【Gitlab】CICD使用minio作为分布式缓存
  • 【OJ】前K个高频单词和单词识别和两个数组的交集
  • PyG教程:MessagePassing基类
  • Java ConcurrentHashMap
  • HTTP 1
  • Java Collection
  • uniapp连接mqtt频繁断开原因和解决方法
  • 【组成原理】计算机硬件设计——ALU
  • Maven 配置