【C++ STL】string模拟实现

简介: 本文将讲述怎么模拟实现string类,有些同学可能会问了,我要实现这个有什么用?会用不就可以了吗?

前言

本文将讲述怎么模拟实现string类,有些同学可能会问了,我要实现这个有什么用?会用不就可以了吗?


你没有错,但是我们通过模拟实现string类可以帮助我们更加深入的了解字符串具体是怎么一回事?它的内部结构是怎么样的?如果以后我们写程序,碰到字符串某个地方报错,也能很快排查出问题哦~


🕺作者: 迷茫的启明星

专栏:《C++初阶》


😘欢迎关注:👍点赞🙌收藏✍️留言


🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要,有问题可在评论区提出,感谢阅读!!!


持续更新中~


string类 的模拟实现

一,搭建框架

我们首先把string类大概样子搭建出来,它不外乎构造函数、拷贝函数,析构函数、还有一些基于C语言实现的方便我们操作字符串的函数,那么我们就来搭建一下,需要提醒的是:一些注意事项我会放在代码的注释中便于理解。

#pragma once
#include <cstring>
#include <iostream>
#include <cassert>
using namespace std;
namespace hxq
{
    class string
    {
        public:
        typedef char* iterator;
        typedef const char* const_iterator;
        iterator begin()
        {
            return _str;//返回首字符地址
        }
        iterator end()
        {
            //字符串一个字符占一个字节
            return _str + _size; 
        }
        //当然也有const类型
        const_iterator begin() const
        {
            return _str;//返回首字符地址
        }
        const_iterator end() const
        {
            //字符串一个字符占一个字节
            return _str + _size;//返回尾巴的地址  
        }
  const char* c_str() const
        {
            return _str;//返回字符串首地址
        }
        size_t size() const
        {
            return _size;//返回字符串长度
        }
        size_t capacity() const
        {
            return _capacity;//返回string对象空间大小
        }
        //字符串类(String)中的 operator[] 运算符重载函数,
        //用于访问字符串中指定位置上的字符(元素),
        //并返回该字符的引用。
        const char& operator[](size_t pos) const
        {
            assert(pos < _size);
            return _str[pos];
        }
        char& operator[](size_t pos)
        {
            assert(pos < _size);
            return _str[pos];
        }
        //析构函数,释放资源
        ~string()
        {
            delete[] _str;
            _str = nullptr;
            _size=_capacity=0;
        }
        //一般来说我们尽量把参数放在一起
        private:
        size_t _capacity;//实际空间
        size_t _size;//字符串长度(不包括'\0')
        char* _str;//首字符地址
        public:
        const static size_t npos=-1;
        //特殊值
        //使用const static 语法修饰
        //npos是最后一个字符的位置
    };
}


二,重载输入输出操作符 ‘<<’ ‘>>’

1. 重载操作符 ‘<<’

将字符逐个输到输出流中


ostream& operator<<(ostream& out, const string& s)
    {
        for (char i : s)
        {
            out << i;
        }
        return out;
    }


2.重载操作符 ‘>>’

且看方式一

按照一般思路,我们设计一个循环读取字符直到遇到空格或者回车停止


于是就有了以下实现


istream& operator>>(istream& in ,string& s)
    {
        char ch;
        ch=in.get();
        s.reserve(128);//预开128个字符的空间
        while(ch!=' '&&ch!='\n')
        {
            s+=ch;
            ch=in.get();
        }
        return in;
    }


但是在测试的时候出现这样的问题


当string对象有默认值时,输入的字符串会加在后面


就像这样:


image.png


结果:

image.png



不仅如此,当输入字符串很长,不断+=,频繁扩容,代码效率很低


来看方式二

我们开辟一个缓存字符数组来存储输入的字符串


达到条件就把缓存空间最后一个值赋值为‘\0’


再尾加缓存空间,也就是尾加字符串

istream& operator>>(istream& in,string& s)
    {
        s.clear();
        char ch;
        ch=in.get();
        const size_t N = 32;
        char buffer[N];
        size_t i =0;
        //如果没有遇到空格或者回车就一直读取到buffer缓存区中
        while(ch != ' '&&ch != '\n')
        {
            buffer[i++] = ch;
            //当它满了就让s尾加buffer
            //注意看buffer经过处理也是一个字符串
            if(i == N-1)
            {
                buffer[i]='\0';
                s+=buffer;
                i=0;
            }
            ch=in.get();
        }
        //读取buffer中剩余字符
        buffer[i]='\0';
        s+=buffer;
        return in;
    }



三,实现构造函数

如何构造一个string的对象呢?


得考虑到三个成员变量:


_str 字符串的地址

_size 字符串长度

_capacity 字符串空间

我们需要将它们初始化


于是以下代码就出现了


string()
    :_str(new char[1])//先预存一个'\0'的空间
        ,_size(0)
        ,_capacity(0)
    {
        _str[0]='\0';
    }


有些同学就会想不能这样实现吗?


string()
    :_str(nullptr)//它没有给\0 预留空间
        ,_size(0)
        ,_capacity(0)
    {}


不能这样子,它无法初始化空对象


但是如果构造函数有参数,这就不适用了,还要重载一个有参的构造函数,我们为什么不实现一个默认参数为“\0”的构造函数呢?


方式一

//string(const char* str = "\0")//二者都可以
string(const char* str = "")//""相当于"\0"
    :_str(new char[strlen(str)+1])
    , _size(strlen(str))
    , _capacity(strlen(str))
{
  strcpy(_str, str);  
}


但是我们会发现它的初始化的时候调用了三次strlen函数是不是看上去就很拉?

于是就有同学想这样了↓

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


不能这样,初始化的顺序是按照成员变量的顺序依次进行的


_str => _capacity => _size


在初始化_str的时候,_capacity是随机值,就不可行


但是我们可以这样,把初始化放在里面,就不会受到它的限制


方式二

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


四,实现拷贝构造和重载赋值操作符

传统的写法

1.拷贝构造

s2(s1)


按照我们之前对拷贝构造的理解


我们需要将s1的’_str’,‘_size’,'_capacity’依次赋值给s2的成员变量


注意:capacity要加1,为‘\0’预留位置


实现:

string(const string& s)
    :_str(new char[s._capacity+1])
        ,_size(s.size())
        ,_capacity(s._capacity)
    {
        strcpy(_str,s._str);
    }


2. 重载赋值操作符

假如说是:s1=s3


思路:


新开辟一个空间tmp,将s3._str赋值给tmp

将s1的_str删除

将tmp的值给s1

string& operator=(const string&s)
{
    if(this!=&s)//判断是否是自己给自己赋值
    {
        //新开一个空间来存s3的值
        char* tmp = new char[s._capacity+1];//多开一个给'\0'
        strcpy(tmp,s._str);
        //把delete[] _str放在开空间后面,避免空间不足,会友好一些
        delete[] _str;//删除原来s1的值
        //将tmp的值给s1
        _str = tmp;
        _size = s._size;
        _capacity = s._capacity;
    }
    return *this;
}



现代写法

1.拷贝构造

还有一种现代写法

这是一种有着资本主义或者说老板思维,让员工干活

具体怎么回事呢?

s2(s1)

我们可以新建一个string对象,并且以s1._str来初始化它

也就是说利用了前文的构造函数string(const char* str = “”)

再将”员工“的’_str’,‘_size’,'_capacity’与this的相交换

这样我们就可以借助这个”员工“来实现拷贝构造

需要注意的是,这里需要进行初始化为nullptr,0,0的操作

因为被调用的员工被处理(析构)的时候_str不能是随机空间(脏数据)(感兴趣的同学可以试着自己试试)


string(const string&s)
    :_str(nullptr)
        ,_size(0)
        ,_capacity(0)
    {
        string tmp(s._str);
        swap(_str,tmp._str);
        swap(_size,tmp._size);
        swap(_capacity,tmp._capacity);
    }


但是它似乎有点不太美观,我们将他放到一个函数中


void swap(string& tmp)//这是我们定义的函数
{
    //↓这是库里面的函数,我们以后再讲
    ::swap(_str,tmp._str);
    ::swap(_size,tmp._size);
    ::swap(_capacity,tmp._capacity);
}
string(const string&s)
    :_str(nullptr)
        ,_size(0)
        ,_capacity(0)
    {
        string tmp(s._str);
        swap(tmp);
        //相当于this->swap(tmp)
    }


2. 重载赋值操作符

再想想,刚刚的赋值操作符是不是也有相似之处呢?


我们只要和拷贝构造那样”雇佣“一个”员工“帮助我们实现即可


可以让代码非常简洁,就像这个式子:s1=s2


我们不需要考虑后面那个值的下场,因为它只是走个过场


所以直接调用swap函数交换它们的’_str’,‘_size’,‘_capacity’


string& operator=(string s)
{
    swap(s);
    return *this;
}


五,实现string 类相关的函数

1. reserve函数

在 C++ 的 string 类中,reserve() 是一个成员函数,


它用于在字符串中预分配一定的内存空间,以提高字符串操作的效率。


具体来说,reserve() 函数的功能是控制字符串对象的容量(capacity),


也就是分配给字符串对象的内存空间大小。当字符串对象的长度超过容量时,


会导致再次分配内存空间,从而影响性能。


因此,通过使用 reserve() 函数可以提前预留一定的内存空间,


以避免频繁地重新分配内存,从而提高程序的运行效率。


实现思路:


如果需要分配的值大于字符串的_capacity就申请空间tmp

将原来的字符串按字节拷贝给tmp

删除原来的字符串的值

将tmp的地址给_str

代码:


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


2.resize函数

在 C++ 的 string 类中,resize() 是一个成员函数,


它用于重新设置字符串的长度(size),并可以选择是否填充新添加的字符。


具体来说,resize() 函数的功能是控制字符串对象的长度和内容。


当需要改变字符串对象的长度时,可以使用 resize() 函数来进行操作。


如果新的长度小于或等于原长度,则会删除超出部分的字符;


如果新的长度大于原长度,则会在末尾添加新的字符。


实现思路:


分为两种情况

如果新的长度大于原长度就申请空间再将新的字符添加即可

如果新的长度小于或等于原长度,则会删除超出部分的字符

代码:

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



3. insert函数

在 C++ 的 string 类中,insert() 是一个成员函数,


它用于将一个字符串或字符序列插入到另一个字符串的指定位置。


具体来说,insert() 函数的功能是在字符串对象的指定位置插入一个字符串或字符序列。


可以通过传递不同的参数组合来选择需要插入的内容和插入位置。


在这里我们仅实现两种常用的


1.插入字符

实现思路:


insert(size_t pos,char ch)


pos>字符串长度报错

如果空间不够扩容

将pos位置后面的字符往后挪

代码:


//使用引用,提高工作效率
string& 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;
    return *this;
}


2.插入字符串

实现思路:


insert(size_t pos,const char* str)


和插入单个字符类似

pos>字符串长度报错

如果空间不够扩容

将pos位置后面的字符往后挪

注意挪的长度是插入字符串的长度

代码:

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 + len;
    while(end>=pos+len)
    {
        _str[end]=_str[end - len];
        --end;
    }
    strncpy(_str + pos,str,len);
    _size += len;
    return *this;
}



4.push_back函数

尾插


实现思路:


push_back(char ch)

1

方式一:判断空间是否足够,在最后设置需要增加字符,再在其后加上‘\0’即可

方式二可以直接使用前面的insert函数进行尾插即可

代码:


//方式一
void push_back(char ch)
{
    if(_size == _capacity)
    {
        reserve(_capacity == 0 ? 4 : _capacity * 2);
    }
    _str[_size]=ch;
    ++_size;
    _str[_size] = '\0';
}
//方式二
void push_back(char ch)
{
    insert(_size,ch);
}


5. append函数

用来将一个字符串追加到另一个字符串的末尾,使其成为一个更长的字符串。


实现思路:


append(const char*str)


方式一:判断原字符串空间是否足够,直接将追加的字符串拷贝在它后面

方式二:使用前面的insert函数尾插字符串

代码:


//方式一
void append(const char*str)
{
    //方式一
    size_t len = strlen(str);
    if (_size+len>_capacity)
    {
        reserve(_size+len);
    }
    strcpy(_str+_size,str);
    //还有一个函数是strcat(_str,str);
    //但是思考一下它的实现方式就会发现它首先要找'\0',那么就不高效
    _size+=len;
}
//方式二
void append(const char*str)
{
    insert(_size,str);
}


6.操作符 += 重载

相当于尾插字符和尾插字符串


实现思路:


string& operator+=(char ch)


尾插字符利用push_back函数

string& operator+=(const char* str)


尾插字符串利用append函数

代码:


//加字符
string& operator+=(char ch)
{
    push_back(ch);
    return *this;
}


//加字符串
string& operator+=(const char* str)
{
    append(str);
    return *this;
}


7.erase函数

从pos开始删除长度为len的字符串,默认npos即为删到尾


实现思路:


erase(size_t pos , size_t len = npos)


pos>字符串长度报错

如果是最后一个或者大于字符串的长度,那么就在pos这个位置上设为’\0’

否则将位置[pos+len]后面的字符串给到[pos]位置,复制结束时再在尾巴上给一个’\0’

代码:

void erase(size_t pos , size_t len = npos)
{
    assert(pos<_size);
    //如果是最后一个或者大于字符串的长度,那么就在pos这个位置上设为'\0'
    if(len == npos || pos + len >= _size)
    {
        _str[pos] = '\0';
        _size = pos;
    }
    else
    {
        //将位置[pos+len]后面的字符串给到[pos]位置
        //复制结束时再在尾巴上给一个'\0'
        strcpy(_str + pos,_str+pos+len);
        _size -=len;
    }
}



8.clear函数

字符清空,长度置为0


实现思路:


clear()


利用字符串以‘\0’结尾的性质

直接将字符串第一个位置置为‘\0’

再将长度置为0

代码:

void clear()
{
    _str[0] = '\0';
    _size = 0;
}


9.find函数

从pos位置开始寻找返回子串在字符串中第一次出现的位置,如果没有找到,则返回 std::string::npos


实现思路:

//找某个字符
size_t find(char ch,size_t pos = 0) const

遍历字符串,寻找匹配字符

//找匹配的字符串
size_t find(const char* sub,size_t pos = 0)const


利用C语言库中的strstr函数

strstr 函数返回的指针指向的是源字符串中第一次出现子串的位置

代码:


//找某个字符
size_t find(char ch,size_t pos = 0) const
{
    assert(pos<_size);
    for(size_t i= pos;i<_size;++i)
    {
        if(ch == _str[i])
        {
            return i;
        }
    }
    return npos;
}
//找匹配的字符串
size_t find(const char* sub,size_t pos = 0)const
{
    assert(sub);
    assert(pos<_size);
    //strstr 函数返回的指针指向的是源字符串中第一次出现子串的位置
    const char* ptr = strstr(_str+pos,sub);
    if(ptr == nullptr)
    {
        return npos;
    }
    else
    {
        return ptr-_str;
    }
}


10.substr函数

用于从字符串中提取子串


实现思路:


string substr(size_t pos,size_t len = npos) const


新建一个string对象

从pos位置开始,截取长度为len的字符串

代码:


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


11.操作符 > == >= <= < != 重载

实现思路:


利用strcmp函数判断大小


代码:


bool operator>(const string&s)const
{
    return strcmp(_str,s._str)>0;
}
bool operator==(const string& s) const
{
    return strcmp(_str, s._str) == 0;
}
bool operator>=(const string& s) const
{
    return *this>s||*this == s;
}
bool operator<=(const string& s) const
{
    return !(*this>s);
}
bool operator<(const string& s) const
{
    return !(*this >= s);
}
bool operator!=(const string& s) const
{
    return !(*this == s);
}



到此我们就模拟了string类的大部分功能,还有一些都是相关的函数,我就不再过多赘述了,这里面最重要的就是构造函数和拷贝构造,一定要理解清楚!


源码:

namespace hxq
{
    class string
    {
        //框架
    public:
        typedef char* iterator;
        typedef const char* const_iterator;
        iterator begin()
        {
            return _str;//返回首字符地址
        }
        iterator end()
        {
            return _str + _size;  //字符串一个字符占一个字节
        }
        //当然也有const类型
        const_iterator begin() const
        {
            return _str;//返回首字符地址
        }
        const_iterator end() const
        {
            return _str + _size;  //字符串一个字符占一个字节
        }
        string(const char* str = "")
        {
            _size = strlen(str);
            _capacity = _size;
            _str = new char[_capacity + 1];
            strcpy(_str, str);
        }
        //拷贝构造
        //s2(s1)
        //传统写法
//        string(const string& s)
//            :_str(new char[s._capacity+1])
//            ,_size(s.size())
//            ,_capacity(s._capacity)
//        {
//            strcpy(_str,s._str);
//        }
//        //假设说是这样的式子:s1=s3
//        string& operator=(const string&s)
//        {
//            if(this!=&s)//判断是否是自己给自己赋值
//            {
//                //新开一个空间来存s3的值
//                char* tmp = new char[s._capacity+1];//多开一个给'\0'
//                strcpy(tmp,s._str);
//                //把delete[] _str放在开空间后面,避免空间不足,会友好一些
//                delete[] _str;//删除原来s1的值
//                //将tmp的值给s1
//                _str = tmp;
//                _size = s._size;
//                _capacity = s._capacity;
//            }
//            return *this;
//        }
//        string(const string&s)
//                :_str(nullptr)
//                ,_size(0)
//                ,_capacity(0)
//        {
//            string tmp(s._str);
//            swap(_str,tmp._str);
//            swap(_size,tmp._size);
//            swap(_capacity,tmp._capacity);
//        }
        //但是它似乎有点不太美观,我们将他放到一个函数中
        //再简化
        void swap(string& tmp)//这是我们定义的函数
        {
            //↓这是库里面的函数,我们以后再讲
            ::swap(_str,tmp._str);
            ::swap(_size,tmp._size);
            ::swap(_capacity,tmp._capacity);
        }
        string(const string&s)
            :_str(nullptr)
            ,_size(0)
            ,_capacity(0)
        {
            string tmp(s._str);
            swap(tmp);
            //相当于this->swap(tmp)
        }
        string& operator=(string s)
        {
            swap(s);
            return *this;
        }
        const char* c_str() const
        {
            return _str;
        }
        //一些函数
        //分配空间
        void reserve(size_t n)
        {
            if(n > _capacity)
            {
                char* tmp=new char[n+1];
                strcpy(tmp,_str);
                delete[] _str;
                _str = tmp;
                _capacity = n;
            }
        }
        //重新设置长度,后面参数如果有的话就是加n个ch
        void resize(size_t n, char ch = '\0')
        {
            if (n > _size)
            {
                reserve(n);
                for (size_t i = _size; i < n; ++i) {
                    _str[i] = ch;
                }
                _str[n]='\0';
                _size = n;
            }
            else
            {
                _str[n]='\0';
                _size = n;
            }
        }
        //插入字符
        //使用引用,提高工作效率
        string& 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;
            return *this;
        }
        //插入字符串
        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 + len;
            while(end>=pos+len)
            {
                _str[end]=_str[end - len];
                --end;
            }
            strncpy(_str + pos,str,len);
            _size += len;
            return *this;
        }
        void push_back(char ch)
        {
            //方式一
//            if(_size == _capacity)
//            {
//                reserve(_capacity == 0 ? 4 : _capacity * 2);
//            }
//
//            _str[_size]=ch;
//            ++_size;
//            _str[_size] = '\0';
            //方式二
            insert(_size,ch);
        }
        void append(const char*str)
        {
            //方式一
            size_t len = strlen(str);
            if (_size+len>_capacity)
            {
                reserve(_size+len);
            }
            strcpy(_str+_size,str);
            //strcat(_str,str);
            //思考一下它的实现方式就会发现它首先要找'\0',那么就不高效
            _size+=len;
            //方式二
            insert(_size,str);
        }
        //加字符
        string& operator+=(char ch)
        {
            push_back(ch);
            return *this;
        }
        //加字符串
        string& operator+=(const char* str)
        {
            append(str);
            return *this;
        }
        //删除
        //从pos开始删除长度为len的字符串,默认npos即为删到尾
        void erase(size_t pos , size_t len = npos)
        {
            assert(pos<_size);
            //如果是最后一个或者大于字符串的长度,那么就在pos这个位置上设为'\0'
            if(len == npos || pos + len >= _size)
            {
                _str[pos] = '\0';
                _size = pos;
            }
            else
            {
//                将位置[pos+len]后面的字符串给到[pos]位置
//                复制结束时再在尾巴上给一个'\0'
                strcpy(_str + pos,_str+pos+len);
                _size -=len;
            }
        }
        //想到字符串是以'\0'判断结束的,于是_str[0] = '\0',再把_size设为0结束
        void clear()
        {
            _str[0] = '\0';
            _size = 0;
        }
        //找某个字符
        size_t find(char ch,size_t pos = 0) const
        {
            assert(pos<_size);
            for(size_t i= pos;i<_size;++i)
            {
                if(ch == _str[i])
                {
                    return i;
                }
            }
            return npos;
        }
        //找匹配的字符串
        size_t find(const char* sub,size_t pos = 0)const
        {
            assert(sub);
            assert(pos<_size);
            //strstr 函数返回的指针指向的是源字符串中第一次出现子串的位置
            const char* ptr = strstr(_str+pos,sub);
            if(ptr == nullptr)
            {
                return npos;
            }
            else
            {
                return ptr-_str;
            }
        }
        //截取字符串
        string substr(size_t pos,size_t len = npos) const
        {
            assert(pos<_size);
            size_t realLen = len;
            if (len == npos || pos+len>_size)
            {
                realLen = _size - pos;
            }
            string sub;
            for (size_t i = 0; i < realLen; ++i) {
                sub+=_str[pos+i];
            }
            return sub;
        }
        bool operator>(const string&s)const
        {
            return strcmp(_str,s._str)>0;
        }
        bool operator==(const string& s) const
        {
            return strcmp(_str, s._str) == 0;
        }
        bool operator>=(const string& s) const
        {
            return *this>s||*this == s;
        }
        bool operator<=(const string& s) const
        {
            return !(*this>s);
        }
        bool operator<(const string& s) const
        {
            return !(*this >= s);
        }
        bool operator!=(const string& s) const
        {
            return !(*this == s);
        }
        //框架
        size_t size() const
        {
            return _size;
        }
        size_t capacity() const
        {
            return _capacity;
        }
        const char& operator[](size_t pos) const
        {
            assert(pos < _size);
            return _str[pos];
        }
        char& operator[](size_t pos)
        {
            assert(pos < _size);
            return _str[pos];
        }
        ~string()
        {
            delete[] _str;
            _str = nullptr;
            _size=_capacity=0;
        }
    //一般来说我们尽量把参数放在一起
    private:
        size_t _capacity;//实际空间
        size_t _size;//字符串长度(不包括'\0')
        char* _str;//首字符地址
    public:
        const static size_t npos=-1;
        //特殊值
        //使用const static 语法修饰
        //npos是最后一个字符的位置
    };
    //重载操作符<<  按照字符逐字节输到输出流中
    ostream& operator<<(ostream& out, const string& s)
    {
        for (char i : s)
        {
            out << i;
        }
        return out;
    }
    //方式一:输入字符串很长,不断+=,频繁扩容,效率很低
    //而且会出现这样的情况,当string对象有默认值时,输入的字符串会加在后面
//    istream& operator>>(istream& in ,string& s)
//    {
//        char ch;
//        ch=in.get();
//        s.reserve(128);
//        while(ch!=' '&&ch!='\n')
//        {
//            s+=ch;
//            ch=in.get();
//        }
//        return in;
//    }
    //方式二
    //设计一个缓存数组
    istream& operator>>(istream& in,string& s)
    {
        s.clear();
        char ch;
        ch=in.get();
        const size_t N = 32;
        char buffer[N];
        size_t i =0;
        //如果没有遇到空格或者回车就一直读取到buffer缓存区中
        while(ch != ' '&&ch != '\n')
        {
            buffer[i++] = ch;
            //当它满了就让s尾加buffer
            //注意看buffer经过处理也是一个字符串
            if(i == N-1)
            {
                buffer[i]='\0';
                s+=buffer;
                i=0;
            }
            ch=in.get();
        }
        //读取buffer中剩余字符
        buffer[i]='\0';
        s+=buffer;
        return in;
    }
}



后记

感谢大家支持!!!


respect!


下篇见

相关文章
|
5天前
|
算法 C++ 容器
【C++】string模拟实现
【C++】string模拟实现
10 1
|
1天前
|
存储 C++ 容器
C++:STL - set & map
C++:STL - set & map
10 4
|
2天前
|
编译器 C++
【C++】继续学习 string类 吧
首先不得不说的是由于历史原因,string的接口多达130多个,简直冗杂… 所以学习过程中,我们只需要选取常用的,好用的来进行使用即可(有种垃圾堆里翻美食的感觉)
7 1
|
2天前
|
算法 安全 程序员
【C++】STL学习之旅——初识STL,认识string类
现在我正式开始学习STL,这让我期待好久了,一想到不用手撕链表,手搓堆栈,心里非常爽
9 0
|
2天前
|
存储 安全 测试技术
【C++】string学习 — 手搓string类项目
C++ 的 string 类是 C++ 标准库中提供的一个用于处理字符串的类。它在 C++ 的历史中扮演了重要的角色,为字符串处理提供了更加方便、高效的方法。
10 0
【C++】string学习 — 手搓string类项目
|
3天前
|
存储 C++ 容器
【C++从练气到飞升】09---string语法指南(二)
【C++从练气到飞升】09---string语法指南(二)
|
3天前
|
存储 Linux C语言
【C++从练气到飞升】09---string语法指南(一)
【C++从练气到飞升】09---string语法指南(一)
|
3天前
|
存储 Serverless C++
【C++入门到精通】哈希 (STL) _ unordered_map _ unordered_set [ C++入门 ]
【C++入门到精通】哈希 (STL) _ unordered_map _ unordered_set [ C++入门 ]
8 1
|
4天前
|
存储 设计模式 算法
【C++/STL】stack和queue(容器适配器、优先队列、双端队列)
【C++/STL】stack和queue(容器适配器、优先队列、双端队列)
11 1
|
4天前
|
存储 编译器 C++
【C++/STL】list(常见接口、模拟实现、反向迭代器、)
【C++/STL】list(常见接口、模拟实现、反向迭代器、)
5 0