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

【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;};
}
  • _str在堆上开辟动态内存用来存储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)是对象复制操作中的一种,它不仅仅复制对象的表层数据(如指针或引用),还会递归地复制对象内部所有动态分配的内存、引用的其他对象或其他资源。这样,复制出来的新对象与原始对象在内存中是完全独立的,对它们的修改不会影响到彼此。

深拷贝的必要性

当对象包含指向动态分配内存的指针或其他需要管理的资源时,浅拷贝(仅复制指针值)会导致两个对象共享同一块内存。这可能会引发以下问题:

  1. 数据损坏:一个对象修改了它共享的内存中的数据,导致另一个对象看到的数据也发生了变化。
  2. 内存泄漏:如果两个对象都认为自己拥有这块内存,并在析构时尝试释放它,就会导致重复释放内存的错误(double free),进而可能导致程序崩溃。
  3. 资源管理混乱:如果对象还管理其他资源(如文件句柄、网络连接等),共享这些资源可能会导致资源被意外关闭或重复访问。

深拷贝的实现

实现深拷贝通常涉及以下几个步骤:

  1. 为新对象分配内存:如果原始对象包含动态分配的内存,深拷贝的第一步是为新对象分配相应的内存空间。
  2. 复制数据:将原始对象中的数据复制到新分配的内存中。如果数据本身也是对象(即对象包含指向其他对象的指针),则需要递归地应用深拷贝。
  3. 更新指针:将新对象的指针成员指向新分配的内存,而不是原始对象的内存。
  4. 处理其他资源:如果对象管理其他资源(如文件、网络连接等),则需要确保新对象也能正确地获取或创建这些资源的副本。

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类--探索字符串操作的底层逻辑

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨文章所属专栏&#xff1a;c篇–CSDN博客 文章目录 前言一.string类的默认成员函数以及深拷贝1.基本框架2.默认成员函数…...

springboot配置logback.xml遇到的几个问题

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

MySQL 5.7与MySQL 8.0对比

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

【代码随想录Day55】图论Part07

prim 算法精讲 题目链接/文章讲解&#xff1a;代码随想录 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月软考马上就要开始了&#xff0c;但是&#xff0c;还有很多的考生&#xff0c;可能还不知道自己到底应该去了解些什么&#xff1f;本文将详细介绍机考注意事项及系统操作提示&#xff0c;帮助考生们备考无忧。 一、考试入场要求和考场规则 1、入场时间&#xff1a;考生需提…...

CMake知识点

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

git ls-remote

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

低代码平台如何通过AI赋能,实现更智能的业务自动化?

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

计算疫情扩散时间

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

【Windows11】24H2 内存占用高(截至10月31日)

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

题目:多个字符从两端移动,向中间汇聚

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

前端如何安全存储密钥,防止信息泄露

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

银行电子户分账解决电商行业哪些问题

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

Web音乐库:SpringBoot实现的音乐网站

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

Rust: 加密算法库 ring 如何用于 RSA 数字签名?

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

Matplotlib 网格线

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

钉钉机器人禅道消息通知@指派人

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

我的新书出版啦!和大家聊聊写书的酸甜苦辣

我的新书出版啦&#xff01;小伙伴们问是不是赚翻了&#xff1f; 大家好&#xff0c;我是码哥。我的新书《Redis 高手心法》出版后&#xff08;2024 年 8 月份出版&#xff09;&#xff0c;有一些小伙伴问了我一些问题&#xff1a; 写书是不是赚了很多钱&#xff1f;我也想写…...

【福建医科大学附属第一医院-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞 …...

第二届新生程序设计竞赛热身赛(C语言)

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

WebSocket和HTTP请求的区别

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

【Python · Pytorch】人工神经网络 ANN(中)

【Python Pytorch】人工神经网络 ANN&#xff08;中&#xff09; 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

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

在做题中学习(72):最小栈

解法&#xff1a;pair<int,int>解决 思路&#xff1a;stack里存pair&#xff0c;push时&#xff0c;first存当前值&#xff0c;而每次push都要更新pair的second&#xff0c;使它成为更小值&#xff0c;最后的getmin&#xff0c;只用取top().second即可拿到最小值。 cla…...

详解软件设计中分库分表的几种实现以及应用示例

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

随着飞行汽车的亮相,在环保方面有什么保护措施吗

飞行汽车具备环保潜力&#xff0c;采用电动或混合动力系统减少污染&#xff0c;并拓展应用场景。多家企业布局&#xff0c;沃飞长空作为国内eVTOL(电动垂直起降航空器)研发的领先企业&#xff0c;在环保这一点做的非常到位&#xff0c;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 是一种网络通信协议&#xff0c;提供了在单个TCP连接上进行全双工通信的能力。这意味着客户端和服务器可以同时发送和接收数据&#xff0c;而不需要等待对方的回应。WebSocket 协议在2011年成为国际标准&#xff0c;并且被大多数现代浏览器所支持。…...

【FinalShell问题】FinalShell连接虚拟机超时问题

问题描述&#xff1a; 打开fianlshell连接之前设置好的SSH连接&#xff08;Linux&#xff09;&#xff0c;结果连接不上&#xff0c;并出现提示&#xff1a;java.net.ConnectException: Connection timed out: connect&#xff0c;说明虚拟机连接超时。 问题解决&#xff1a;…...