目录
一.引言
二.string类的容量操作接口
三.string类的字符串修改操作接口
1.两个插入字符的重载函数:
2.在string字符串末尾追加内容的接口
3.在指定位置pos删除n个字符的接口
四.string类字符串的字符和子串查找接口
五.全局定义的string类字符串比较运算符重载
六.全局用于string类的流插入和流提取运算符重载
一.引言
书接上回string的模拟实现(上):http://t.csdn.cn/Lphxv
- 关于函数代码复用的编程思维
在编写复杂的项目时,应尽可能在编写函数模块时复用已经编写好的函数,而且复用函数应遵循同源复用的原则:
这种同源复用的编程思维可以带来诸多好处:
- 可以使代码看起来更简洁美观,可读性大大增强
- 同源复用相当于划定了一个错误池(比如上图中的函数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'; }
注意:
- 形参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) { 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; }