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

C++:string模拟实现(下)

目录

一.引言

二.string类的容量操作接口

三.string类的字符串修改操作接口

1.两个插入字符的重载函数:

2.在string字符串末尾追加内容的接口

3.在指定位置pos删除n个字符的接口

四.string类字符串的字符和子串查找接口

五.全局定义的string类字符串比较运算符重载

     六.全局用于string类的流插入和流提取运算符重载


一.引言

书接上回string的模拟实现(上):http://t.csdn.cn/Lphxv

  •  关于函数代码复用的编程思维

在编写复杂的项目时,应尽可能在编写函数模块时复用已经编写好的函数,而且复用函数应遵循同源复用的原则:

这种同源复用的编程思维可以带来诸多好处:

  1. 可以使代码看起来更简洁美观,可读性大大增强
  2. 同源复用相当于划定了一个错误池(比如上图中的函数1),整个复用链只要有一个地方出错,我们都可以一步一步追溯查错直到复用链的源头去修改代码,从而提高了代码的可维护性。

模拟string类的类域代码总览:

#include<iostream>
#include<cstring>
#include <assert.h>
using std :: ostream;
using std :: istream;
using std::cout;
using std::cin;
using std::endl;
//只要不涉及寻址,一定要注意编译器的顺序编译机制

namespace mystring
{



    class string
    {
        public:
            typedef char * iterator;                   //string类的迭代器宏定义
            typedef const char* const_iterator;



            string(const char* nstr);                  //类中四个重要的默认成员函数
            string(const string& nstr);
            ~string();
            string& operator=(string nstr);

            
            void push_back (const char& c);            //在字符串末尾追加字符的功能接口
            void append(const char * str);             //在字符串末尾追加字符串的功能接口
            string& operator+=(char c);                //在字符串末尾追加字符的功能接口
            string& operator+=(const char * str);      //在字符串末尾追加字符串的功能接口
            string& insert (size_t pos , char c);      //pos位置插入一个字符
            string& insert (size_t , const char *str); //pos位置插入一个字符串
            string& erase(size_t pos, size_t n);       //pos位置删除n个字符




            void reserve (size_t newcapacity);         //扩容接口
            void resize(size_t newsize,const char c);  //调整有效字符个数的接口(多出的有效 
                                                       //字符用c填补)










            void copyswap(string &nstr);               //复用交换函数的接口
            iterator begin();                          //string类用于获取迭代器的接口
            iterator end();
            const_iterator begin()const;
            const_iterator end()const;
            const char * C_str()const;                 //用于返回C类型字符串的函数
            char & operator[](int pos);                //用于返回pos下标字符的引用的[]重载
            const char & operator[](int pos) const;     
            size_t size() const;                       //获取有效字符个数size和容量的接口
            size_t capacity() const;
            size_t find(char c,size_t pos =0);         //从第pos个位置查找字符串中第一 
                                                       //个出现c字符的位置
            size_t find(const char * str , size_t pos =0);  //从第pos个位置查找字符串中第一 
                                                            //个出现子串str的位置






        private:
            char * _str;
            size_t _size;
            size_t _capacity;
    };

二.string类的容量操作接口

void reserve (size_t newcapacity);         //扩容接口
void resize(size_t newsize,const char c);  //调整有效字符个数的接口(多出的有效字符用c填补)
  • void reserve (size_t newcapacity);扩容接口(只扩容,不缩容)
    void mystring::string::reserve (size_t newcapacity) //扩容接口
    {
        if(newcapacity > _capacity)
        {
            char * tem = new char[newcapacity+1]; // 重新申请堆空间调整容量,+1是为了保存'\0'
            strcpy(tem,_str);                     // 原空间字符串拷贝新空间
            delete[]_str;                         // 释放原空间
            _str = tem;
            _capacity = newcapacity;
        }
    }

  • void resize(size_t newsize,const char c);

    void mystring::string::resize(size_t newsize,const char c = '\0') 
    {
        if(newsize > _capacity)         //调整有效数字个数前检查是否需要扩容
        {
            reserve(newsize);           //复用扩容接口
        }
        while(newsize > _size)          //如果有效字符个数增大,多余有效字符容量补上c字符
        {
            _str[_size] = c;
            _size ++;
        }
        _size = newsize;
        _str[_size]='\0';
    }

注意:

  1. 形参char c最好给上'\0'作为缺省值
  2. string的字符串末尾(最后一个有效字符的后一个空间)要补上'\0'以兼容C的字符串

三.string类的字符串修改操作接口

1.两个插入字符的重载函数:

 string& insert (size_t pos , char c);      //pos位置插入一个字符
 string& insert (size_t , const char *str); //pos位置插入一个字符串
  • pos位置插入一个字符:string& insert (size_t pos , char c);      
    string& mystring::string::insert (size_t pos , char c)    //pos位置插入一个字符
    {
        assert(pos <= _size);
        if(_size == _capacity)
        {
            reserve(0==_capacity? 4 : 2*_capacity);
        }
        for(int i=_size+1;i>=pos+1 ; i--)    //将pos位置后的所有字符向后移一位
        {                                                                    
            _str[i]=_str[i-1];
        }
        _str[pos]=c;
        _size ++;
        return (*this);
    }

注意:

  1. 扩容时要判断当前容量是否为零,若不为0则按两倍增长的方式扩容
  2. for循环的判断条件中pos是无符号数,表达式进行比较时左边的 i 会发生隐式类型转换转换为无符号数,因此不能让 i 减为-1(-1的补码全为1,换算为无符号数是32位整数的最大值),否则循环会跑死。

  • pos位置插入一个字符串 :string& insert (size_t , const char *str); 
    string& mystring::string::insert (size_t pos, const char *str) //pos位置插入一个字符串
    {
        assert(pos <= _size);
        size_t len = strlen(str);                       //求出待插入的字符串的字符个数
        if(len+_size > _capacity)                       //插入字符串前检查是否要扩容
        {
            reserve(len+_size);
        }
        for(int i = _size+len;i>=pos+len;i--)           //将pos+len后面的字符向后移动len位
        {                                               //(\0 也被移动了)
            _str[i]= _str[i-len];
        }
        for(int i=pos;i<len+pos;i++)                    //将待插入字符串拷贝到pos位置
        {
            _str[i]=str[i-pos];
        }
        _size = len+_size;
        return (*this);
    }

2.在string字符串末尾追加内容的接口

  • 在字符串末尾追加字符的功能接口:void push_back (const char& c); 

通过复用insert即可实现:

    void mystring::string::push_back (const char& c)
    {
        insert(_size,c);
    }

  • 在字符串末尾追加字符串的功能接口:void append(const char * str);     

通过复用insert即可实现:

    void mystring::string::append(const char * str)
    {
        insert(_size,str);
    }

  •   在字符串末尾追加字符的运算符重载:string& operator+=(char c);                

通过复用push_back即可实现:

    string& mystring::string::operator+=(char c)            //在字符串末尾追加字符的功能接口
    {
        push_back(c);
        return (*this);
    }

  • 在字符串末尾追加字符串的运算符重载:string& operator+=(const char * str);      

通过复用append即可实现:

    string& mystring::string::operator+=(const char * str) //在字符串末尾追加字符串的功能接口
    {
        append(str);
        return (*this);
    }

3.在指定位置pos删除n个字符的接口

  •  在pos位置删除n个字符:string& erase(size_t pos, size_t n); 

   删除pos位置后的字符分两种情况讨论

  1.  pos位置后的字符全部删除
  2.  pos位置后的n个字符被删除

STL中规定当n等于-1(string中宏定义为npos)时,删除pos位置后的全部字符

    string& mystring::string::erase(size_t pos=0 , size_t n = -1)      //pos位置删除n个字符
    {
        assert(pos<_size);
        if(n>=_size-pos || -1 == n)                 //删除pos位置后的所有字符
        {
            _size = pos;
            _str[pos]='\0';
            return (*this);
        }
        else
        {                                           //删除pos位置后的n个字符
            for(int i= pos + n ; i<= _size ; i++)   //将pos+n位置后的字符往前挪n位(包括\0)
            {
                _str[i-n]=_str[i];
            }
            _size = _size -n;
            return (*this);
        }
    }

四.string类字符串的字符和子串查找接口

//从第pos个位置查找字符串中查找第一个出现c字符的位置
size_t find(char c,size_t pos =0);


//从第pos个位置查找字符串中第一个出现子串str的位置
size_t find(const char * str , size_t pos =0);
 
                                                            
    size_t mystring::string::find(char c,size_t pos)                  
    {
        assert(pos < _size);
        int i=0;
        for(i=pos;i<_size;i++)
        {
            if(c==_str[i])
            {
                return i;
            }
        }
        return -1;
    }
    size_t mystring::string::find(const char * str, size_t pos)
    {
        assert(pos < _size);
        const char * strpos = strstr(_str+pos,str);
        if(nullptr == strpos)
        {
            return -1;
        }
        return strpos - _str;        //指针相减得到字串出现的位置
    }

五.全局定义的string类字符串比较运算符重载

  • 重载>运算符
bool operator>(const mystring :: string& str1 , const mystring :: string& str2)
{
    int ch1 = 0;
    int ch2 = 0;
    while(ch1<str1.size() && ch2 < str2.size())
    {
        if(str1[ch1] > str2[ch2])
        {
            return true;
        }
        else if(str1[ch1]<str2[ch2])
        {
            return false;
        }
        else
        {
            ch1++;
            ch2++;
        }
    }
    return ch1==str1.size()? false : true;
}

注意:

当while循环结束函数还没有返回时,ch1和ch2的可能值有三种情况:

  1. ch1 == str1.size()      ch2 ! = str2.size()    返回false
  2. ch1 ! = str2.size()      ch2 == str2.size()    返回true
  3. ch1 == str1.size()      ch2  == str2.size()   返回false

  • 重载==运算符
bool operator==(const mystring :: string& str1 , const mystring :: string& str2)
{
    int ch1 =0;
    int ch2 =0;
    while(ch1<str1.size() && ch2 < str2.size())
    {
        if(str1[ch1]!= str2[ch2])
        {
            return false;
        }
        else
        {
            ch1++;
            ch2++;
        }
    }
    return ch1==ch2? true : false;
}

  • 其余的比较运算符重载可以通过复用实现
bool operator>=(const mystring :: string& str1 , const mystring :: string& str2)
{
    return (str1 > str2) || (str1 == str2);
}
bool operator<(const mystring :: string& str1 , const mystring :: string& str2)
{
    return !(str1 >= str2);
}
bool operator<=(const mystring :: string& str1 , const mystring :: string& str2)
{
    return !(str1 > str2);
}

六.全局用于string类的流插入和流提取运算符重载

std :: ostream & operator<<(std :: ostream& cout , const mystring :: string& str )
{
    int i =0;
    for(i=0;i<str.size();i++)
    {
        cout << str[i];
    }
    return cout;
}
std :: istream & operator>>(std::istream & cin , mystring :: string & str)
{
    char ch = cin.get();
    while(ch != '\n')
    {
        str += ch ;
        ch = cin.get();
    }
    return cin;
}

模拟实现代码:

#include<iostream>
#include<cstring>
#include <assert.h>
using std :: ostream;
using std :: istream;
using std::cout;
using std::cin;
using std::endl;
//只要不涉及寻址,一定要注意编译器的顺序编译机制

namespace mystring
{



    class string
    {
        public:
            typedef char * iterator; 
            typedef const char* const_iterator;



            string(const char* nstr);                  //类中四个重要的默认成员函数
            string(const string& nstr);
            ~string();
            string& operator=(string nstr);

            
            void push_back (const char& c);            //在字符串末尾追加字符的功能接口
            void append(const char * str);             //在字符串末尾追加字符串的功能接口
            string& operator+=(char c);                //在字符串末尾追加字符的功能接口
            string& operator+=(const char * str);      //在字符串末尾追加字符串的功能接口
            string& insert (size_t pos , char c);      //pos位置插入一个字符
            string& insert (size_t , const char *str); //pos位置插入一个字符串
            string& erase(size_t pos, size_t n);       //pos位置删除n个字符




            void reserve (size_t newcapacity);         //扩容接口
            void resize(size_t newsize,const char c);  //调整有效数字个数的接口(多出的有效 
                                                       //字符用c填补)




            void copyswap(string &nstr);               //复用交换函数的接口
            iterator begin();                          //string类用于获取迭代器的接口
            iterator end();
            const_iterator begin()const;
            const_iterator end()const;
            const char * C_str()const;                 //用于返回C类型字符串的函数
            char & operator[](int pos);                //用于返回pos下标字符的引用的[]重载
            const char & operator[](int pos) const;     
            size_t size() const;                       //获取有效字符个数size和容量的接口
            size_t capacity() const;
            size_t find(char c,size_t pos =0);         //从第pos个位置查找字符串中第一个出 
                                                       //现c字符的位置
            size_t find(const char * str , size_t pos =0);  //从第pos个位置查找字符串中第一 
                                                            //个出现子串str的位置






        private:
            char * _str;
            size_t _size;
            size_t _capacity;
    };

    
    
    template <typename T>
    void myswap (T& e1 , T&e2)
    {
        T tem = e1;
        e1 = e2;
        e2 =tem;
    }
    void mystring::string::copyswap(string & nstr)
    {
        myswap(_str,nstr._str);
        myswap(_size,nstr._size);
        myswap(_capacity,nstr._capacity);
    }
    const char * mystring::string::C_str() const
    {
        return _str;
    }
    

    mystring::string::string(const char* nstr = "")  //缺省值用于构造空字符串(会完成'\0'的 
                                         //拷贝)(注意缺省参数只能写在声明和定义其中一个之中)
    :_size(strlen(nstr))                             //strlen函数中有关于空指针的断言,所以 
                                                     //构造函数中无须进行空指针判断
    ,_capacity (_size)
    {                          
        _str = new char[_capacity+1];                // +1是为了保存'\0'(不计入容量和有效字 
                           //符),同时保证_str不为空指针(对象只要创建出来就一定维护一块堆空间)
        strcpy(_str,nstr);
    }
    // mystring::string::string(const string& nstr)  //非复用式写法
    // :_size(nstr._size)
    // ,_capacity(nstr._capacity)
    // {
    //     _str = new char[_capacity+1];             // +1是为了保存'\0'(不计入容量和有效 
                                                     //字符),同时保证_str不为空指针
    //     strcpy(_str,nstr._str);
    // }
    mystring::string::string(const string& nstr)     //复用构造函数完成拷贝构造
    :_str (nullptr)                                  //防止tem中_str作为野指针被释放
    ,_size(0)
    ,_capacity(0)
    {
        string tem(nstr._str);                       //拷贝构造拷贝的是不含'\0'的有效字符
        this->copyswap(tem);                         //为了方便阅读加上this指针
    }


    // string& mystring::string::operator=(const string& nstr)
    // {
    //     if(this != &nstr)                             //判断重载操作数是否为同一个对象
    //     {
    //         char * tem = new char[nstr._capacity+1];  // +1是为了保存'\0'(不计入容量和 
                                                         //有效字符)
    //         strcpy(tem,nstr._str);
    //         delete[] _str;
    //         _str = tem;
    //         _size = nstr._size;
    //         _capacity = nstr._capacity;
    //     }
    //     return *this;
    // }
    string& mystring::string::operator=(string nstr)     //与直接交换对象作对比
    {
        copyswap(nstr);
        return (*this);
    }
    mystring::string::~string()
    {
        if(_str)
        {
            delete[] _str;
            _str=nullptr;
        }
        _size=0;
        _capacity=0;
    }




    mystring::string::iterator mystring::string:: begin()
    {
        return _str;
    }
    mystring::string::iterator mystring::string::end()
    {
        return _str+_size; 
    }
    mystring::string::const_iterator mystring::string::begin()const
    {
        return _str;
    }
    mystring::string::const_iterator mystring::string::end()const
    {
        return _str+_size;
    }




    char &  mystring::string::operator[](int pos)
    {
        assert(pos<_size);                                 //越界报警
        return _str[pos];
    }
    const char &  mystring::string::operator[](int pos) const
    {
        assert(pos<_size);
        return _str[pos];
    }


    size_t mystring::string::size() const
    {
        return _size;
    }
    size_t mystring::string::capacity() const
    {
        return _size;
    }






    void mystring::string::reserve (size_t newcapacity)             //扩容接口
    {
        if(newcapacity > _capacity)
        {
            char * tem = new char[newcapacity+1];                   // +1是为了保存'\0'
            strcpy(tem,_str);
            delete[]_str;
            _str = tem;
            _capacity = newcapacity;
        }
    }
    void mystring::string::resize(size_t newsize,const char c = '\0') //调整有效数字个数的 
                                                             //接口(多出的有效字符用c填补)
    {
        if(newsize > _capacity)
        {
            reserve(newsize);
        }
        while(newsize > _size)
        {
            _str[_size] = c;
            _size ++;
        }
        _size = newsize;
        _str[_size]='\0';
    }









    // void mystring::string::push_back (const char& c)             //在字符串末尾追加字符 
                                                                    //的功能接口
    // {
    //     if(_size == _capacity)                                   //检查是否需要增容
    //     {
    //         reserve((0==_capacity? 4 : 2*_capacity));            //注意增容前判断容量是 
                              //否为零,为了减少后续可能的增容次数,这里按照两倍扩大的方式增容
    //     }
    //     _str[_size]=c;
    //     _size++;
    //     _str[_size]='\0';                                        //记得在末尾有效字符后 
                                                                    //面补上'\0'
    // }

    void mystring::string::push_back (const char& c)
    {
        insert(_size,c);
    }
    // void mystring::string::append(const char * str)
    // {
    //     size_t temsize = strlen(str)+_size;
    //     if(temsize > _capacity)
    //     {
    //         reserve(temsize);
    //     }
    //     strcpy(_str + _size , str);                              //追加字符串
    //     _size = temsize;
    // }
    void mystring::string::append(const char * str)
    {
        insert(_size,str);
    }
    string& mystring::string::operator+=(char c)           //在字符串末尾追加字符的功能接口
    {
        push_back(c);
        return (*this);
    }
    string& mystring::string::operator+=(const char * str) //在字符串末尾追加字符串的功能接口
    {
        append(str);
        return (*this);
    }

    string& mystring::string::insert (size_t pos , char c)      //pos位置插入一个字符
    {
        assert(pos <= _size);
        if(_size == _capacity)
        {
            reserve(0==_capacity? 4 : 2*_capacity);
        }
        for(int i=_size+1;i>=pos+1 ; i--)                     //注意细节:隐式类型转换
        {
            _str[i]=_str[i-1];
        }
        _str[pos]=c;
        _size ++;
        return (*this);
    }
    string& mystring::string::insert (size_t pos, const char *str) //pos位置插入一个字符串
    {
        assert(pos <= _size);
        size_t len = strlen(str);
        if(len+_size > _capacity)
        {
            reserve(len+_size);
        }
        for(int i = _size+len;i>=pos+len;i--)
        {
            _str[i]= _str[i-len];
        }
        for(int i=pos;i<len+pos;i++)
        {
            _str[i]=str[i-pos];
        }
        _size = len+_size;
        return (*this);
    }
    string& mystring::string::erase(size_t pos=0 , size_t n = -1)     //pos位置删除n个字符
    {
        assert(pos<_size);
        if(n>=_size-pos || -1 == n)
        {
            _size = pos;
            _str[pos]='\0';
            return (*this);
        }
        else
        {
            for(int i= pos + n ; i<= _size ; i++)
            {
                _str[i-n]=_str[i];
            }
            _size = _size -n;
            return (*this);
        }
    }














    size_t mystring::string::find(char c,size_t pos)    //查找字符串中第一个出现c字符的位置
    {
        assert(pos < _size);
        int i=0;
        for(i=pos;i<_size;i++)
        {
            if(c==_str[i])
            {
                return i;
            }
        }
        return -1;
    }
    size_t mystring::string::find(const char * str, size_t pos)
    {
        assert(pos < _size);
        const char * strpos = strstr(_str+pos,str);
        if(nullptr == strpos)
        {
            return -1;
        }
        return strpos - _str;
    }


}


//  str1  abc
//  str2  ab

//全局的stream对象的比较运算符重载
bool operator>(const mystring :: string& str1 , const mystring :: string& str2)
{
    int ch1 = 0;
    int ch2 = 0;
    while(ch1<str1.size() && ch2 < str2.size())
    {
        if(str1[ch1] > str2[ch2])
        {
            return true;
        }
        else if(str1[ch1]<str2[ch2])
        {
            return false;
        }
        else
        {
            ch1++;
            ch2++;
        }
    }
    return ch1==str1.size()? false : true;
}

bool operator==(const mystring :: string& str1 , const mystring :: string& str2)
{
    int ch1 =0;
    int ch2 =0;
    while(ch1<str1.size() && ch2 < str2.size())
    {
        if(str1[ch1]!= str2[ch2])
        {
            return false;
        }
        else
        {
            ch1++;
            ch2++;
        }
    }
    return ch1==ch2? true : false;
}
bool operator>=(const mystring :: string& str1 , const mystring :: string& str2)
{
    return (str1 > str2) || (str1 == str2);
}
bool operator<(const mystring :: string& str1 , const mystring :: string& str2)
{
    return !(str1 >= str2);
}
bool operator<=(const mystring :: string& str1 , const mystring :: string& str2)
{
    return !(str1 > str2);
}




//全局的stream对象的流插入,流提取运算符重载
std :: ostream & operator<<(std :: ostream& cout , const mystring :: string& str )
{
    int i =0;
    for(i=0;i<str.size();i++)
    {
        cout << str[i];
    }
    return cout;
}
std :: istream & operator>>(std::istream & cin , mystring :: string & str)
{
    char ch = cin.get();
    while(ch != '\n')
    {
        str += ch ;
        ch = cin.get();
    }
    return cin;
}

相关文章:

  • 记录一次sql group by 优化记录
  • 静图怎么做成gif动画图片?三步教你在线生成gif图片
  • 华为OD测试岗面经,一周走完面试流程
  • ESP32+Arduino+OLED+u8g2播放视频
  • let/const相关内容(二)
  • 【Classical Network】HRNet
  • SaaS-API越权漏洞检测系统
  • 【Flume】Flume原理简述及示例实践
  • 程序员想兼职赚钱?这几个渠道你一定要知道?
  • Hystrix高可用框架
  • 【Python百日进阶-Web开发-Vue3】Day505 - 自动创建Vue项目
  • 点云处理指南介绍
  • 抓包对抗原理与案例
  • Android中级——滑动分析
  • 阅读腾讯云SDK文档心得
  • 2023年PMP 具体的考试时间是什么时候?
  • 3-3-多线程-TheadLocal内存泄漏
  • 基于SIFT的图像Matlab拼接教程
  • 孙子兵法总结
  • Axios用法总结(附有封装好的axios请求)
  • 电加热油锅炉工作原理_电加热导油
  • 大型电蒸汽锅炉_工业电阻炉
  • 燃气蒸汽锅炉的分类_大连生物质蒸汽锅炉
  • 天津市维修锅炉_锅炉汽化处理方法
  • 蒸汽汽锅炉厂家_延安锅炉厂家
  • 山西热水锅炉厂家_酒店热水 锅炉
  • 蒸汽锅炉生产厂家_燃油蒸汽发生器
  • 燃煤锅炉烧热水_张家口 淘汰取缔燃煤锅炉
  • 生物质锅炉_炉
  • 锅炉天然气_天燃气热风炉