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

【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[]打印s1
    for(size_t i=0;i<s1.size();i++){
        cout<<s1[i]<<" ";
    }
    cout<<endl;

    //使用普通对象的迭代器打印s1
    Mystring::string::iterator it=s1.begin();
    while(it!=s1.end()){
        cout<<*it<<" ";
        it++;
    }
    cout<<endl;

    //使用const对象的迭代器打印s2
    const 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容量扩为15
    s1.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(){
    //创建一个空对象s1
    Mystring::string s1;
    //s1追加字符串hello
    s1.append("hello");
    cout<<s1<<endl;
    //s1追加字符串world
    s1+="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[]打印s1
    for(size_t i=0;i<s1.size();i++){
        cout<<s1[i]<<" ";
    }
    cout<<endl;

    //使用普通对象的迭代器打印s1
    Mystring::string::iterator it=s1.begin();
    while(it!=s1.end()){
        cout<<*it<<" ";
        it++;
    }
    cout<<endl;

    //使用const对象的迭代器打印s2
    const 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容量扩为15
    s1.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(){
    //创建一个空对象s1
    Mystring::string s1;
    //s1追加字符串hello
    s1.append("hello");
    cout<<s1<<endl;
    //s1追加字符串world
    s1+="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类的讲解,如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!
在这里插入图片描述


http://www.kler.cn/a/373533.html

相关文章:

  • ROS(快速初步入门)
  • 移远通信闪耀2024香港秋灯展,以丰富的Matter产品及方案推动智能家居产业发展
  • 【Linq】在C#中从SQL query case语句发布到linq扩展方法
  • SSRF-pikachu
  • TLKS-PMG-100BM这款输电线路智能多目视频监控装置,它具体有哪些亮点和优势?
  • MaxKB: 一款基于大语言模型的知识库问答系统
  • oracle获取中文拼音/WB
  • isp框架代码理解
  • python debug作业
  • 前端vue2迁移至uni-app
  • 产品宣传册制作成电子产品宣传册用什么软件?
  • ollama 在 Linux 环境的安装
  • Leetcode 79 Word search
  • 保障农民工工资!我们这么做:
  • 前端面试题-token的登录流程、JWT
  • Django+Vue智慧分析居家养老系统统的设计与实现
  • 【Vulnhub靶场】DC-5
  • 构建旋转变换矩阵对二维到高维空间的线段点进行旋转
  • 微信小程序app.js里面onLaunch里面的函数比page里面的onshow里面的方法后执行
  • 接口表笔记
  • SchooWeb2--基于课堂学习到的知识点2
  • java基础面试题三异常处理
  • 技术总结(十七)
  • 什么是成品系统源码,哪里有成品源码,成品源码二次开发需要多久?
  • 小白学大模型:斯坦福CS25 Transformers与LLMs
  • QT 周期性的杀死一个进程(软件),一分钟后自动退出