【c++篇】:模拟实现string类--探索字符串操作的底层逻辑
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨
✨ 个人主页:余辉zmh–CSDN博客
✨文章所属专栏:c++篇–CSDN博客
文章目录
- 前言
- 一.`string`类的默认成员函数以及深拷贝
- 1.基本框架
- 2.默认成员函数
- 1.构造函数
- 2.析构函数
- 3.拷贝构造函数(深拷贝)
- 4.赋值运算符重载(深拷贝)
- 3.什么是深拷贝
- 深拷贝的必要性
- 深拷贝的实现
- 4.测试
- 二.`string`类的访问和迭代器相关函数
- 1.访问相关函数
- 2.迭代器相关函数
- 1.普通`string`对象的迭代器
- 2.`const` `string`对象的迭代器
- 3.测试
- 三.`string`类的容量相关函数
- 1.容量大小相关函数
- 2.扩容相关函数
- 3.测试
- 四.`string`类的修改相关函数
- 1.拼接操作相关函数
- 2.插入操作相关函数
- 3.删除操作相关函数
- 4.测试
- 五.`string`类的流插入和流提取函数
- 六.`string`类完整代码
- 1.`string.h`头文件代码
- 2.`string.cpp`函数定义文件代码
- 3.`test.cpp`测试文件代码
前言
在上一篇文章中,我们了解到了std::string
类的常用接口函数以及如何熟练使用。在这片文章中,我们将深入探讨如何模拟实现一个基本的string
类。我们的目的不是创建一个功能完整的string
库,而是通过这个过程来学习字符串处理的基本原理和常见的实现技巧。通过模拟实现string
类,我们不仅能够深入理解字符串的内部工作原理,还能锻炼我们的编程能力,提高解决问题的能力,希望这篇文章能过为你的编程之旅提供有价值的参考和启发。
注意:模拟实现string类需要用到三个文件
test.cpp
文件用来进行测试string.cpp
文件用来定义接口函数(部分较为短小的函数将会直接在string.h
文件中定义)string.h
文件用来声明头文件和string类
一.string
类的默认成员函数以及深拷贝
1.基本框架
为了和库里面的std::string
类进行区分,我们首先定义一个命名空间Mystring
用来封装我们自己模拟实现的string
类。
基本框架如下:
namespace Mystring{class string{Public:static const size_t npos=-1;//成员函数private://成员变量char* _str;size_t _size;size_t capacity;};
}
_st
r在堆上开辟动态内存用来存储string对象的数据。_size
用来记录string对象的实际大小。_capacity
用来记录当前string对象可存储的最大容量。npos
是一个无符号整形的最大值,在查找等相关函数会用到。
2.默认成员函数
前面我们知道,一个类中有六个默认成员函数(在我之前的文章类和对象(二)中有关于默认成员函数的详细讲解,不清楚的可以看我之前的文章),在模拟实现string类时,我们只需要实现常用的四个(构造函数,析构函数,拷贝构造函数,赋值运算符重载)即可。
1.构造函数
-
代码实现:
//string.h中声明 string(const char* str = ""); //string.cpp中定义 Mystring::string::string(const char* str) :_size(strlen(str)) ,_capacity(_size) ,_str(new char[_size+1]) {memcpy(_str,str,_size+1); }
-
实现原理:
- str字符串作为常量参数用来创建string对象,缺省值为空字符串(当没有参数时就是创建一个空对象),缺省值要在声明中给,不能再定义中给。
- _size字符串大小和_capacity容量初始化值为参数str字符串的大小(strlen(str))。
- _str指针用来指向存储string对象的动态内存,开辟空间的大小为参数str字符串的大小加一,加一是为了存放结尾的’\0’,开辟空间后要将str字符串内容拷贝到开辟的动态内存。
2.析构函数
-
代码实现:
//string.h文件中声明 ~string(); //string.cpp文件中定义 Mystring::string::~string(){delete[] _str;_str=nullptr;_size=0;_capacity=0; }
-
实现原理:
- 释放动态内存空间,再将_str指针置为空指针。
- _size字符串大小和_capacity容量置为0。
3.拷贝构造函数(深拷贝)
-
代码实现:
//string.h文件中声明 string(const string&s); //string.cpp文件中定义 Mystring::string::string(const string&s) {_str=new char[s._capacity+1];strcpy(_str,s._str);_size=s._size;_capacity=s._capacity; }
-
实现原理:
- 用一个string对象拷贝构造一个新的string对象,拷贝构造函数需要完成深拷贝。
- 先用字符串s的容量作为新空间的大小申请一个新的动态内存空间,再将s字符串的数据拷贝到新的内存空间中。
- 新的string对象的_size和_capacity分别是原string对象的_size和_capacity.
4.赋值运算符重载(深拷贝)
-
代码实现:
//string.h文件中声明 string& operator=(const string&s); //string.cpp文件中定义 Mystring::string& Mystring::string::operator=(const string&s) {char* tmp=new char[s._capacity+1];memcpy(tmp,s._str,s._size+1);delete[] _str;_str=tmp;_capacity=s._capacity;_size=s._size;return *this; }
-
实现原理:
- 赋值和拷贝构造的不同点在于,拷贝构造是用已有的对象拷贝构造一个新的对象;而赋值是用一个已存在的对象赋值给另一个已存在的对象,赋值也是需要完成深拷贝。
- 先用赋值对象s的容量大小申请一个新的动态内存空间,设置一个新的tmp指针先指向这块空间,再将赋值对象s的数据拷贝到新的内存空间中。
- 释放被赋值对象的原有空间,再将被赋值对象的_str指针指向新的内存空间。
- 被赋值对象的_size和_capacity分别是赋值对象s的_size和_capacity.
3.什么是深拷贝
深拷贝(Deep Copy)是对象复制操作中的一种,它不仅仅复制对象的表层数据(如指针或引用),还会递归地复制对象内部所有动态分配的内存、引用的其他对象或其他资源。这样,复制出来的新对象与原始对象在内存中是完全独立的,对它们的修改不会影响到彼此。
深拷贝的必要性
当对象包含指向动态分配内存的指针或其他需要管理的资源时,浅拷贝(仅复制指针值)会导致两个对象共享同一块内存。这可能会引发以下问题:
- 数据损坏:一个对象修改了它共享的内存中的数据,导致另一个对象看到的数据也发生了变化。
- 内存泄漏:如果两个对象都认为自己拥有这块内存,并在析构时尝试释放它,就会导致重复释放内存的错误(double free),进而可能导致程序崩溃。
- 资源管理混乱:如果对象还管理其他资源(如文件句柄、网络连接等),共享这些资源可能会导致资源被意外关闭或重复访问。
深拷贝的实现
实现深拷贝通常涉及以下几个步骤:
- 为新对象分配内存:如果原始对象包含动态分配的内存,深拷贝的第一步是为新对象分配相应的内存空间。
- 复制数据:将原始对象中的数据复制到新分配的内存中。如果数据本身也是对象(即对象包含指向其他对象的指针),则需要递归地应用深拷贝。
- 更新指针:将新对象的指针成员指向新分配的内存,而不是原始对象的内存。
- 处理其他资源:如果对象管理其他资源(如文件、网络连接等),则需要确保新对象也能正确地获取或创建这些资源的副本。
4.测试
测试代码如下:
void test1(){//创建s1对象Mystring::string s1("hello world");//用s1对象拷贝构造s2对象Mystring::string s2(s1);//将s1对象赋值给s3对象Mystring::string s3("hello");s3=s1;
}
测试结果如下:
二.string
类的访问和迭代器相关函数
1.访问相关函数
-
at()
函数代码实现://string文件中声明和定义 char& at(size_t pos) {//断言检查pos是否符合字符串的范围assert(pos<_size);//直接返回字符数组对应下标上的字符即可return _str[pos]; }
-
operator[]
函数代码实现://string文件中声明和定义 //和at()函数同理 char& operator[](size_t pos) {assert(pos<_size);return _str[pos]; }
2.迭代器相关函数
1.普通string
对象的迭代器
-
类型定义:
//普通string对象的迭代器类型 typedef char* iterator;
-
begin()
函数代码实现://string文件中声明和定义 iterator begin()const {//返回_str首元素的地址return _str; }
-
end()
函数代码实现://string文件中声明和定义 iterator end()const {//返回_str最后一个元素的下一个位置的地址return _str+_size; }
2.const
string
对象的迭代器
-
类型定义:
//const string对象的迭代器类型 typedef const char* const_iterator;
-
cbegin()
函数代码实现://string文件中声明和定义 const_iterator begin()const {//返回_str首元素的地址return _str; }
-
cend()
函数代码实现://string文件中声明和定义 const_iterator end()const {//返回_str最后一个元素的下一个位置的地址return _str+_size; }
3.测试
测试代码如下:
void test2(){Mystring::string s1("hello world");//使用opreator[]打印s1for(size_t i=0;i<s1.size();i++){cout<<s1[i]<<" ";}cout<<endl;//使用普通对象的迭代器打印s1Mystring::string::iterator it=s1.begin();while(it!=s1.end()){cout<<*it<<" ";it++;}cout<<endl;//使用const对象的迭代器打印s2const Mystring::string s2("hello");Mystring::string::const_iterator rit=s2.begin();while(rit!=s2.end()){cout<<*rit<<" ";rit++;}cout<<endl;
}
测试结果如下:
三.string
类的容量相关函数
1.容量大小相关函数
获取string对象的大小需要使用size()函数,获取容量则需要使用capacity()函数
-
size()
函数代码实现://string.h文件中声明和定义 //直接返回_size即可 size_t size() {return _size; }
-
capacity()
函数代码实现://string.h文件中声明和定义 //直接返回_capacity即可 size_t capacity() {return _capacity; }
2.扩容相关函数
string
类扩容相关的函数主要是reserve()
和resize()
。
-
reserve()
函数代码实现://string.h文件中声明 void reserve(size_t); //string.cpp文件中定义 void Mystring::string::reserve(size_t n) {if(n>_capacity){char*tmp=new char[n+1];memcpy(tmp,_str,_size+1);delete[] _str;_str=tmp;_capacity=n;} }
-
实现原理:
- 首先判断需要扩容的大小n是否大于原容量大小_capacity,如果小于则不进行扩容,大于时就需要扩容。
- 扩容时先开辟内存大小为(n+1)的动态内存空间,在设置一个新的字符指针tmp指向新的内存空间,然后将原string对象的数据拷贝到新的内存空间中。
- 释放原string对象的_str指针,再从新指向开辟的新内存空间,最后更改内存大小为n。
-
resize()
函数代码实现://string.h文件中声明 void resize(size_t n,char ch='\0'); //string.cpp文件中定义 void Mystring::string::resize(size_t n,char ch) {if(n>_capacity){reserve(n);}if(n>_size){for(size_t i=_size;i<n;i++){_str[i]=ch;}}_size=n;_str[_size]='\0';}
-
实现原理:
-
首先依然是需要判断扩容的大小n是否大于原容量大小_capacity,和reserve()函数不同的是,resize对于扩容大小n小于原容量大小时会发生截断,大于时就会进行扩容操作,这里扩容操作直接调用reserve()函数即可。
-
如果扩容大小n大于原string对象的_size,需要将多余的空间初始化为参数ch,参数ch的缺省值设置为’\0’(要在声明中给),没有传参时,默认初始化为0。
-
3.测试
测试代码如下:
void test3(){Mystring::string s1("hello world");cout<<s1.size()<<" "<<s1.capacity()<<endl;//s1容量扩为15s1.reserve(15);cout<<s1.size()<<" "<<s1.capacity()<<endl;//s1容量扩为20,多余空间用字符a填充s1.resize(20,'a');cout<<s1.size()<<" "<<s1.capacity()<<endl;cout<<s1<<endl;//s1缩容为8,发生截断s1.resize(8);cout<<s1.size()<<" "<<s1.capacity()<<endl;cout<<s1<<endl;
}
测设结果如下:
四.string
类的修改相关函数
1.拼接操作相关函数
-
1.push_back()函数实现:
//string.h文件中声明 void push_back(char ch); //string.cpp文件中定义 void Mystring::string::push_back(char ch) {if(_size==_capacity){reserve(_capacity==0?4:2*_capacity);}_str[_size]=ch;_size++;_str[_size]='\0'; }
2.实现原理:
- push_back()函数的功能是尾插单个字符,在尾插之前要先判断是否需要扩容,在扩容时,如果需要尾插的string对象是空对象扩容 大小先设置为4,如果不是空对象则扩容大小是原容量的二倍,扩容直接调用reserve()函数即可。
- 插入时,将原来字符串的’\0’位置存放插入的字符ch,再将大小_size增加一,最后重新在字符串的结尾加上’\0’。
-
1.
append()
函数实现:append()函数有两种不同的实现,一个是在原
string
对象后增加单个字符,一个是增加字符串。//string.h文件中声明 void append(char ch); void append(const char* str); //string.cpp文件中定义 //增加单个字符 void Mystring::string::append(char ch) {push_back(ch); } //增加字符串 void Mystring::string::append(const char* str) {size_t len=strlen(str);if(_size+len>_capacity){reserve(_size+len);}//_str加上_size就是原字符串结尾的位置,插入的字符串拷贝到原字符串后面memcpy(_str+_size,str,len+1);_size+=len; }
2.实现原理:
- 增加单个字符直接调用push_back函数。
- 增加字符串时,先获取插入字符串的大小len,然后判断原字符串的大小加上插入字符串的大小是否大于容量_capacity,如果大于需要先进行扩容,扩容大小为原字符串的大小加上插入字符串的大小。
- 将插入的字符串拷贝到原字符串后面,拷贝大小为len+1,然后修改_size值。
-
1.
operator+=
函数实现:operator+=
函数和append()
函数一样有两种不同的实现,在原string
对象后增加单个字符和字符串。//string.h文件中声明 string& operator+=(char ch); string& operator+=(const char* str); //string.cpp文件中定义 //增加单个字符 Mystring::string& Mystring::string::operator+=(char ch) {push_back(ch);return *this; } //增加字符串 Mystring::string& Mystring::string::operator+=(const char* str) {append(str);return *this; }
2.实现原理:
- 增加单个字符直接调用push_back()函数,最后要返回this指针。
- 增加字符串直接调用append(const char* str)函数,最后返回this指针。
2.插入操作相关函数
-
insert()
函数实现:1.插入n个字符:
//string.h文件中声明 void insert(size_t pos,size_t n,char ch); //string.cpp文件中定义 void Mystring::string::insert(size_t pos,size_t n,char ch) {assert(pos<_size);if((_size+n)>_capacity){reserve(_size+n);}size_t end=_size;while(end>=pos&&end!=npos){_str[end+n]=_str[end];end--;}for(size_t i=0;i<n;i++){_str[pos+i]=ch;}_size+=n;}
实现原理:
- 如果原字符串大小加上插入的个数大于容量大小时,需要先进行扩容,扩容大小为原字符串大小加上插入的个数。
- 将插入位置后面的数据依次往后移动n个位置。
- 再将移动后空出来的位置插入n个字符ch。最后修改_size大小。
2.插入字符串:
//string.h文件中声明 void insert(size_t pos,const char*str); //string.cpp文件中定义 void Mystring::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 end=_size;while(end>=pos&&end!=npos){_str[end+len]=_str[end];end--;}for(size_t i=0;i<len;i++){_str[pos+i]=str[i];}_size+=len; }
实现原理:
- 插入字符串和插入n个字符原理相同,不同点是,最后插入时,需要依次插入字符串的字符。
3.删除操作相关函数
-
erase()
函数实现://string.h中声明 void erase(size_t pos,size_t len=npos); //string.cpp文件中定义 void Mystring::string::erase(size_t pos,size_t len) {assert(pos<_size);if(len==npos||pos+len>=_size){_size=pos;_str[_size]='\0';}else{size_t end=len+pos;while(end<=_size){_str[pos++]=_str[end++];}_size-=len;} }
-
实现原理:
- 删除要分为两种情况,第一种,如果删除的个数大于_size或者len等于npos值,直接从pos位置将后面全部删除。
- 第二种,依次将后面的字符往前移动,从而达到删除的目的。
4.测试
测试代码如下:
void test4(){//创建一个空对象s1Mystring::string s1;//s1追加字符串hellos1.append("hello");cout<<s1<<endl;//s1追加字符串worlds1+="world";cout<<s1<<endl;//s1尾插字符!s1.push_back('!');cout<<s1<<endl;//在s1下标为6的位置插入***s1.insert(6,"***");cout<<s1<<endl;//删除s1下标为10后面的所有字符s1.erase(10);cout<<s1<<endl;//删除s1下标为5后面的三个字符s1.erase(5,3);cout<<s1<<endl;
}
测试结果如下:
五.string
类的流插入和流提取函数
-
1.
operator<<
流插入函数实现://string.h文件中声明 ostream& operator<<(ostream& out,const string&s); //string.cpp文件中定义 ostream& Mystring::operator<<(ostream& out,const string&s) {cout<<"operator<<"<<endl;for(auto e:s){out<<e;}return out; }
-
1.
operator>>
流提取函数实现://string.h文件中声明 istream& operator>>(istream &in,string &s); //string.cpp文件中定义 istream& Mystring::operator>>(istream &in, string &s) {cout<<"operator>>"<<endl;s.clear();char ch = in.get();// 处理前缓冲区前面的空格或者换行while (ch == ' ' || ch == '\n'){ch = in.get();}char buff[128];int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i != 0){buff[i] = '\0';s += buff;}return in; }
测试代码如下:
void test5(){Mystring::string s1;//输入cin>>s1;//输出cout<<s1<<endl;
}
六.string
类完整代码
1.string.h
头文件代码
#include<iostream>
#include<string>
#include<string.h>
#include<assert.h>
using namespace std;namespace Mystring{class string{public:static const size_t npos=-1;//构造函数string(const char* str = "");//析构函数~string();//拷贝构造函数string(const string&s);//赋值运算符重载string& operator=(const string&s);//c格式打印const char* c_str(){return _str;}//容量大小size_t size(){return _size;}size_t capacity(){return _capacity;}//扩容void reserve(size_t n);void resize(size_t n,char ch='\0');void clear(){_size=0;_str[_size]='\0';}//访问方式char& operator[](size_t pos){assert(pos<_size);return _str[pos];}char& at(size_t pos){assert(pos<_size);return _str[pos];}//迭代器typedef char* iterator;iterator begin() {return _str;}iterator end() {return _str+_size;}typedef const char* const_iterator;const_iterator begin()const {return _str;}const_iterator end()const {return _str+_size;}//修改void push_back(char ch); void append(char ch); void append(const char* str); string& operator+=(char ch); string& operator+=(const char* str); void insert(size_t pos,size_t n,char ch);void insert(size_t pos,const char*str);void erase(size_t pos,size_t len=npos);//查找size_t find(char ch,size_t pos=0);size_t find(const char*str,size_t pos=0);//友元函数friend ostream& operator<<(ostream& out,const string&s);friend istream& operator>>(istream& in,string&s);private:int _size;int _capacity;char* _str;};//非string成员函数ostream& operator<<(ostream& out,const string&s);istream& operator>>(istream& in,string&s);
}
2.string.cpp
函数定义文件代码
#include"string.h"Mystring::string::string(const char* str)
:_size(strlen(str))
,_capacity(_size)
,_str(new char[_size+1])
{cout<<"string()"<<endl;memcpy(_str,str,_size+1);
}Mystring::string& Mystring::string::operator=(const string&s)
{cout<<"operator="<<endl;char* tmp=new char[s._capacity+1];memcpy(tmp,s._str,s._size+1);delete[] _str;_str=tmp;_capacity=s._capacity;_size=s._size;return *this;
}Mystring::string::~string(){cout<<"~string()"<<endl;delete[] _str;_str=nullptr;_size=0;_capacity=0;
}Mystring::string::string(const string&s)
{cout<<"string()"<<endl;_str=new char[s._capacity+1];strcpy(_str,s._str);_size=s._size;_capacity=s._capacity;
}void Mystring::string::reserve(size_t n)
{if(n>_capacity){char*tmp=new char[n+1];memcpy(tmp,_str,_size+1);delete[] _str;_str=tmp;_capacity=n;}
}void Mystring::string::resize(size_t n,char ch)
{if(n>_capacity){reserve(n);}if(n>_size){for(size_t i=_size;i<n;i++){_str[i]=ch;}}_size=n;_str[_size]='\0';}void Mystring::string::push_back(char ch)
{if(_size==_capacity){reserve(_capacity==0?4:2*_capacity);}_str[_size]=ch;_size++;_str[_size]='\0';
}void Mystring::string::append(char ch)
{push_back(ch);
}void Mystring::string::append(const char* str)
{size_t len=strlen(str);if(_size+len>_capacity){reserve(_size+len);}memcpy(_str+_size,str,len+1);_size+=len;
}Mystring::string& Mystring::string::operator+=(char ch)
{push_back(ch);return *this;
}Mystring::string& Mystring::string::operator+=(const char* str)
{append(str);return *this;
}size_t Mystring::string::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;
}size_t Mystring::string::find(const char*str,size_t pos)
{assert(pos<_size);const char* ptr=strstr(_str+pos,str);if(ptr){return ptr-_str;}else{return npos;}
}void Mystring::string::insert(size_t pos,size_t n,char ch)
{assert(pos<_size);if((_size+n)>_capacity){reserve(_size+n);}size_t end=_size;while(end>=pos&&end!=npos){_str[end+n]=_str[end];end--;}for(size_t i=0;i<n;i++){_str[pos+i]=ch;}_size+=n;}void Mystring::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 end=_size;while(end>=pos&&end!=npos){_str[end+len]=_str[end];end--;}for(size_t i=0;i<len;i++){_str[pos+i]=str[i];}_size+=len;
}void Mystring::string::erase(size_t pos,size_t len)
{assert(pos<_size);if(len==npos||pos+len>=_size){_size=pos;_str[_size]='\0';}else{size_t end=len+pos;while(end<=_size){_str[pos++]=_str[end++];}_size-=len;}
}ostream& Mystring::operator<<(ostream& out,const string&s)
{//cout<<"operator<<"<<endl;for(auto e:s){out<<e;}return out;
}istream& Mystring::operator>>(istream &in, string &s)
{cout<<"operator>>"<<endl;s.clear();char ch = in.get();// 处理前缓冲区前面的空格或者换行while (ch == ' ' || ch == '\n'){ch = in.get();}char buff[128];int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i != 0){buff[i] = '\0';s += buff;}return in;
}
3.test.cpp
测试文件代码
#include"string.h"void test1(){//创建s1对象Mystring::string s1("hello world");//用s1对象拷贝构造s2对象Mystring::string s2(s1);//将s1对象赋值给s3对象Mystring::string s3("hello");s3=s1;
}void test2(){Mystring::string s1("hello world");//使用opreator[]打印s1for(size_t i=0;i<s1.size();i++){cout<<s1[i]<<" ";}cout<<endl;//使用普通对象的迭代器打印s1Mystring::string::iterator it=s1.begin();while(it!=s1.end()){cout<<*it<<" ";it++;}cout<<endl;//使用const对象的迭代器打印s2const Mystring::string s2("hello");Mystring::string::const_iterator rit=s2.begin();while(rit!=s2.end()){cout<<*rit<<" ";rit++;}cout<<endl;
}void test3(){Mystring::string s1("hello world");cout<<s1.size()<<" "<<s1.capacity()<<endl;//s1容量扩为15s1.reserve(15);cout<<s1.size()<<" "<<s1.capacity()<<endl;//s1容量扩为20,多余空间用字符a填充s1.resize(20,'a');cout<<s1.size()<<" "<<s1.capacity()<<endl;cout<<s1<<endl;//s1缩容为8,发生截断s1.resize(8);cout<<s1.size()<<" "<<s1.capacity()<<endl;cout<<s1<<endl;
}void test4(){//创建一个空对象s1Mystring::string s1;//s1追加字符串hellos1.append("hello");cout<<s1<<endl;//s1追加字符串worlds1+="world";cout<<s1<<endl;//s1尾插字符!s1.push_back('!');cout<<s1<<endl;//在s1下标为6的位置插入***s1.insert(6,"***");cout<<s1<<endl;//删除s1下标为10后面的所有字符s1.erase(10);cout<<s1<<endl;//删除s1下标为5后面的三个字符s1.erase(5,3);cout<<s1<<endl;
}void test5(){Mystring::string s1;//输入cin>>s1;//输出cout<<s1<<endl;}int main()
{//test1();//test2();//test3();//test4();test5();return 0;
}
以上就是关于如何模拟实现string类的讲解,如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!
相关文章:

【c++篇】:模拟实现string类--探索字符串操作的底层逻辑
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨ ✨ 个人主页:余辉zmh–CSDN博客 ✨文章所属专栏:c篇–CSDN博客 文章目录 前言一.string类的默认成员函数以及深拷贝1.基本框架2.默认成员函数…...

springboot配置logback.xml遇到的几个问题
最近项目用到对日志脱敏,经过研究通过logback实现了对日志脱敏,上篇文章中详细讲解了如果配置。但是还是对logback的配置不太了解。比如springboot怎么加载这个logback.xml的。 首先,默认情况下,logback.xml文件是放在类目录下&am…...

MySQL 5.7与MySQL 8.0对比
一、功能对比 JSON支持 MySQL 5.7:引入了JSON数据类型,允许用户存储和操作JSON格式的数据,这是NoSQL功能的一个重要补充。但相对于MySQL 8.0,其功能和性能较弱。MySQL 8.0:在JSON支持方面进行了重大改进,引…...

【代码随想录Day55】图论Part07
prim 算法精讲 题目链接/文章讲解:代码随想录 import java.util.*;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);// 读取顶点数和边数int vertexCount scanner.nextInt();int edgeCount scanner.nextI…...

软考在即!这些注意事项你提前了解!
11月软考马上就要开始了,但是,还有很多的考生,可能还不知道自己到底应该去了解些什么?本文将详细介绍机考注意事项及系统操作提示,帮助考生们备考无忧。 一、考试入场要求和考场规则 1、入场时间:考生需提…...

CMake知识点
参考: https://zhuanlan.zhihu.com/p/661284252 cmake使用教程(实操版)-CSDN博客 【CMake】CMake从入门到实战系列(二)——实例入手,讲解CMake的基本流程_cmake创建一个可执行目标的过程-CSDN博客 一、…...

git ls-remote
文章目录 1.简介2.格式3.选项4.示例5.小结参考文献 1.简介 git ls-remote 是一个 Git 命令,用于列出远程 Git 仓库的引用(refs),包括分支、标签等。 这个命令非常有用,可以帮助你查看远程仓库中可用的分支和标签&…...

低代码平台如何通过AI赋能,实现更智能的业务自动化?
引言 随着数字化转型的加速推进,企业在日常运营中面临的业务复杂性与日俱增。如何快速响应市场需求,优化流程,并降低开发成本,成为各行业共同关注的核心问题。低代码平台作为一种能够快速构建应用程序的工具,因其可视化…...

计算疫情扩散时间
该专栏题目包含两部分: 100 分值部分题目 200 分值部分题目 所有题目都会陆续更新,订阅防丢失 题目描述 在一个地图中(地图由 N ∗ N N*N N∗N 个区域组成),有部分区域被感染病菌。 感染区域每天都会把周围(上下左右)的4个区域感染。 请…...

【Windows11】24H2 内存占用高(截至10月31日)
文章目录 一、问题二、解决三、原因 一、问题 系统版本: 内存只有32GB。 以前只有我在运行数据处理程序的时候内存占用才会很高,日常情况下应该只有40%、50%左右的。 但是24H2,日常情况下内存占用80%以上。 而我只开了很少的应用&#…...

题目:多个字符从两端移动,向中间汇聚
【多个字符从两端移动,向中间汇聚】 char arr1[] "Good Good Study,Day Day Up!" ; char arr2[] "***************************"; 【思路】 首先两字符串中的元素个数要相同,将两串字符分别存放在数组中,那么字符串中…...

前端如何安全存储密钥,防止信息泄露
场景 把公钥硬编码在前端代码文件里,被公司安全检测到了要整改,于是整理几种常见的前端密钥存储方案。 1. 设置环境变量再读取 在打包或部署前端应用时,可以将密钥配置为环境变量,在应用运行时通过环境变量读取密钥。这样可以将密…...

银行电子户分账解决电商行业哪些问题
随着电子商务的快速发展,电商银行电子户分账作为金融科技领域的重要一环,逐渐成为现代金融业务的核心。本文将详细探讨电商银行电子户分账的原理、操作流程、安全措施以及在电子商务中的重要作用。 二、电商银行电子户分账的基本概念 电商银行电子户分…...

Web音乐库:SpringBoot实现的音乐网站
2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…...

Rust: 加密算法库 ring 如何用于 RSA 数字签名?
本来用 rsa 库基本搞定,但文心一言建议改用 ring 库。原因是 rsa 库已经放弃维护,而 ring 库性能公认很好。但是如何进行 RSA 数字签名,网上几乎查不到这方面材料。仔细查看了 ring 库的源代码和代码注释,终于完成趟坑。总结一下供…...

Matplotlib 网格线
Matplotlib 网格线 Matplotlib 是一个强大的 Python 绘图库,广泛用于数据可视化。在 Matplotlib 中,网格线是一种常用的辅助工具,用于增强图表的可读性和美观性。本文将详细介绍如何在 Matplotlib 中添加和使用网格线。 1. 简介 网格线是在…...

钉钉机器人禅道消息通知@指派人
钉钉、禅道怎么设置webhook? 点击查看:获取自定义机器人 Webhook 地址 在禅道上配置钉钉机器人webhook,使用管理员账号登录,找到通知设置 添加webhook 添加webhook所需要的数据即可 webhook设置,根据自己的实际…...

我的新书出版啦!和大家聊聊写书的酸甜苦辣
我的新书出版啦!小伙伴们问是不是赚翻了? 大家好,我是码哥。我的新书《Redis 高手心法》出版后(2024 年 8 月份出版),有一些小伙伴问了我一些问题: 写书是不是赚了很多钱?我也想写…...

【福建医科大学附属第一医院-注册安全分析报告】
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞 …...

第二届新生程序设计竞赛热身赛(C语言)
A:饥饿的XP XP迷失在X星球,他醒来时已经很久很久很久没有吃过东西了。他突然发现身边有一张地图,上面有X星球上每一个食物供给点的位置。太好了,XP跳了起来。他决定先把肚子填饱再去寻找其他伙伴。现在已知XP的位置(X, Y),以及他的…...

WebSocket和HTTP请求的区别
1. 连接方式 HTTP请求:基于“请求-响应”模式。每次通信都要重新建立连接,客户端发送请求后服务器返回响应,连接就断开了。这种模式通常适合不频繁更新的数据,如静态页面的加载。WebSocket:支持长连接,连接…...

【Python · Pytorch】人工神经网络 ANN(中)
【Python Pytorch】人工神经网络 ANN(中) 6. 反向传播6.1 梯度下降法6.1.1 线搜索方法6.1.2 微分 & 导数6.1.3 偏导数6.1.4 Jacobian矩阵6.1.5 梯度 & 梯度下降法按维度介绍 6.1.6 面临挑战平原现象 & 振荡现象局部最小值鞍点梯度消失梯度爆…...

穷举vs暴搜vs深搜vs回溯vs剪枝 算法专题
一. 全排列 全排列 class Solution {List<List<Integer>> ret;List<Integer> path;boolean[] check;public List<List<Integer>> permute(int[] nums) {ret new ArrayList<>();//存放结果path new ArrayList<>();存放每个路径的…...

Uni-App-02
条件编译 条件编译概念 不同的运行平台终归有些专有的特性,无法实现跨平台完全兼容,例如:微信小程序导航栏右上角的关闭图标。 uni-app提供了一种“条件编译”机制,可以针对特定的平台编译执行特定的代码,否则不执行。…...

在做题中学习(72):最小栈
解法:pair<int,int>解决 思路:stack里存pair,push时,first存当前值,而每次push都要更新pair的second,使它成为更小值,最后的getmin,只用取top().second即可拿到最小值。 cla…...

详解软件设计中分库分表的几种实现以及应用示例
详解软件设计中分库分表的几种实现以及应用示例https://mp.weixin.qq.com/s?__bizMzkzMTY0Mjc0Ng&mid2247485108&idx1&sn8b3b803c120c163092c70fa65fe5541e&chksmc266aaa1f51123b7af4d7a3113fe7c25daa938a04ced949fb71a8b7773e861fb93d907435386#rd...

随着飞行汽车的亮相,在环保方面有什么保护措施吗
飞行汽车具备环保潜力,采用电动或混合动力系统减少污染,并拓展应用场景。多家企业布局,沃飞长空作为国内eVTOL(电动垂直起降航空器)研发的领先企业,在环保这一点做的非常到位,AE200采用纯电动力系统,零碳排放,静默飞行…...

docker安装、设置非sudo执行、卸载
安装 sudo snap install docker 设置docker非sudo执行 sudo groupadd docker sudo usermod -aG docker $USER newgrp docker sudo chown root:docker /var/run/docker.sock 卸载docker 1.删除docker及安装时自动安装的所有包 apt-get autoremove docker docker-ce docker-…...

WebSocket简单使用
1.WebSocket 简介 WebSocket 是一种网络通信协议,提供了在单个TCP连接上进行全双工通信的能力。这意味着客户端和服务器可以同时发送和接收数据,而不需要等待对方的回应。WebSocket 协议在2011年成为国际标准,并且被大多数现代浏览器所支持。…...

【FinalShell问题】FinalShell连接虚拟机超时问题
问题描述: 打开fianlshell连接之前设置好的SSH连接(Linux),结果连接不上,并出现提示:java.net.ConnectException: Connection timed out: connect,说明虚拟机连接超时。 问题解决:…...