目录
一.引言
二.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;
}