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

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

目录

一.引言

二.string类的容量操作接口

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

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

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

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

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

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

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

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

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

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

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

include

include

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';
}

注意:

形参char c最好给上'\0'作为缺省值
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);
}

注意:

扩容时要判断当前容量是否为零,若不为0则按两倍增长的方式扩容
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位置后的字符分两种情况讨论

pos位置后的字符全部删除
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的可能值有三种情况:

ch1 == str1.size() ch2 ! = str2.size() 返回false
ch1 ! = str2.size() ch2 == str2.size() 返回true
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)
{

str.erase(0,-1);
char ch = cin.get();
while(ch != '\n')
{
    str += ch ;
    ch = cin.get();
}
return cin;

}

模拟实现代码:

include

include

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)
{

str.erase(0,-1);
char ch = cin.get();
while(ch != '\n')
{
    str += ch ;
    ch = cin.get();
}
return cin;

}

相关文章
|
28天前
|
存储 C++ 容器
C++入门指南:string类文档详细解析(非常经典,建议收藏)
C++入门指南:string类文档详细解析(非常经典,建议收藏)
37 0
|
28天前
|
存储 编译器 C语言
C++_String增删查改模拟实现
C++_String增删查改模拟实现
47 0
|
6天前
|
存储 安全 C语言
【C++】string类
【C++】string类
|
存储 编译器 Linux
标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”
标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”
|
8天前
|
编译器 C++
标准库中的string类(上)——“C++”
标准库中的string类(上)——“C++”
|
18天前
|
存储 算法 C语言
【C++初阶】8. STL初阶 + String类
【C++初阶】8. STL初阶 + String类
48 1
|
18天前
|
C语言 C++
【C++初阶】9. string类的模拟实现
【C++初阶】9. string类的模拟实现
38 1
|
23天前
|
NoSQL C++
c++中包含string成员的结构体拷贝导致的double free问题
c++中包含string成员的结构体拷贝导致的double free问题
8 0
|
1月前
|
存储 C++
C++:String的模拟实现
C++:String的模拟实现
|
1月前
|
存储 缓存 编译器
C++:String类的使用
C++:String类的使用