namespace cl
{
//模拟实现string类
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
//默认成员函数
string(const char* str = ""); //构造函数
string(const string& str); //拷贝构造函数
string& operator=(const string& str); //赋值运算符重载函数
~string(); //析构函数
//容量操作函数
size_t size();
size_t capacity();
void reserve(size_t n);
void resize(size_t n, char ch = '\0');
bool empty()const;
//迭代器相关函数
iterator begin();
iterator end();
const_iterator cbegin();
const_iterator cend();
const_iterator begin()const;
const_iterator end()const;
//修改字符串相关函数
string& operator+=(char ch);
string& operator+=(const char* str);
string& insert(size_t pos, char ch);
string& insert(size_t pos, const char* str);
string& erase(size_t pos, size_t len);
void push_back(char ch);
void append(const char* str);
void swap(string& str);
const char* c_str()const;
void clear();
//访问字符串相关函数
char& operator[](size_t i);
const char& operator[](size_t i)const;
size_t find(char ch, size_t pos = 0)const;
size_t find(const char* str, size_t pos = 0)const;
size_t rfind(char ch, size_t pos = npos)const;
size_t rfind(const char* str, size_t pos = 0)const;
//关系运算符重载函数
bool operator>(const string& str)const;
bool operator<(const string& str)const;
bool operator>=(const string& str)const;
bool operator<=(const string& str)const;
bool operator==(const string& str)const;
bool operator!=(const string& str)const;
private:
char* _str; //存储字符串
size_t _size; //记录字符串当前的有效长度,不包含\0
size_t _capacity; //记录字符串当前的容量,不包含\0的大小,但是开辟空间时要包含\0在内
static const size_t npos; //静态成员变量(整型最大值)
};
const size_t string::npos = -1;
//<<和>>运算符重载函数
istream& operator>>(istream& in, string& str);
ostream& operator<<(ostream& out, const string& str);
istream& getline(istream& in, string& str);
}
构造函数就是开辟空间,给成员变量进行初始化。缺省参数就可以少写一个无参构造函数,若不传参默认生成空字符串。空字符串是有大小的包含了’\0’。
string(const char* str = "")
:_size(strlen(str))//strlen会检查空指针
,_capacity(_size)
{
_str=new char[_capacity + 1];
strcpy(_str, str);
}
传统写法:
string(const string& str)
:_str(new char[strlen(str._str) + 1]) //_str申请一块刚好可以容纳str._str的空间
, _size(0)
, _capacity(0)
{
strcpy(_str, str._str);
_size = str._size;
_capacity = str._capacity;
}
现代写法:
//现代写法
string(const string& str)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(str._str);//出了作用域对象调用析构
swap(tmp);//交换对象
}
传统写法:
string& operator=(const string& str)
{
// 没有处理异常情况,错误情况
/*if (this != &str)//防止自己给自己赋值
{
delete[] _str;
_str = nullptr;
_str = new char[_capacity + 1];
strcpy(_str, str._str);
_size=str.size;
_capacity=str._capacity;
}*/
if (this != &str)//防止自己给自己赋值
{
char* tmp = _str = new char[_capacity + 1];
strcpy(tmp, str._str);
delete[] _str;
_str = tmp;
_size = str._size;
_capacity = str._capacity;
}
return *this;
}
注:这里需要先申请空间,如果申请空间失败会抛异常跳到处理异常,不会执行后面的代码,所以先申请空间,发生错误时就不会影响到原有的对象。
现代写法:
通过编译器自动调用拷贝构造一个对象str,然后交换对象。
//现代写法一、
string& operator=( string str)
{
swap(str);
return *this;
}
现代写法一、不能判断自己给自己赋值,自己给自己赋值影响也不是很大,就是最后的空间地址发生了变化。
//现代写法2
string& operator=(const string& str)
{
if (this != &str) //防止自己给自己赋值
{
string tmp(str); //用s拷贝构造出对象tmp
swap(tmp); //交换这两个对象
}
return *this; //返回左值(支持连续赋值)
}
析构函数需要我们写,因为string类对象都需要开辟动态内存的资源,动态开辟的空间需要我们手动清理。
~string()
{
delete[] _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
string 的迭代器实际就是char*的别名。
typedef char* iterator;
typedef const char* const_iterator;
// 非const对象调用
iterator begin()
{
return _str;
}
// 非const对象调用
iterator end()
{
return _str + _size;
}
// const对象调用
const_iterator begin()const
{
return _str;
}
// const对象调用
const_iterator end()const
{
return _str + _size;
}
const_iterator cbegin()
{
return _str;
}
const_iterator cend()
{
return _str + _size;
}
注意:end()返回的迭代器执行末尾’\0’。
范围for的原理:把范围for的代码替换成迭代器访问。
string str("hello BBQ");
//编译器将其替换为迭代器形式
for (auto val : str)
{
cout << val << " ";
}
cout << endl;
auto it = s1.begin();
while (it != s1.end())
{
*it += 1;
++it;
}
size和capacity
//大小
size_t size()const
{
return _size; //返回字符串当前的有效长度
}
//容量
size_t capacity()const
{
return _capacity; //返回字符串当前的容量
}
reserve和resize
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n+1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void resize(size_t n, char ch = '\0')
{
if (n > _size)
{
reserve(n);
memset(_str + _size, ch, n - _size);
}
_size = n;
_str[_size] = '\0';
}
insert
string& insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity==0?4:2*_capacity);
}
size_t end = _size+1;//+1:'\0'也进行处理,后面就不需要末尾添加'\0'了
while (pos < end)
{
_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 (len + _size > _capacity)
{
reserve(len + _size);
}
size_t end = _size + len;//+1:'\0'也进行处理,后面就不需要末尾添加'\0'了
while (pos+len <= end)
{
_str[end] = _str[end - len];
end--;
}
strncpy(_str + pos, str, len);//拷贝时不包含'\0'
_size += len;
return *this;
}
push_back
void push_back(char ch)
{
/*if (_size = _capacity)
{
reserve(2 * _capacity);//增容两倍
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
*/
insert(_size, ch);
}
append
void append(const char* str)
{
/*size_t len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size);
}
strcpy(_str + _size, str);
_size += len;*/
insert(_size, str);
}
operator+=
string& operator+=(const string& str)
{
append(str._str);
return *this;
}
erase
string& erase(size_t pos = 0, size_t len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
swap
void swap( string& str)
{
std::swap(_size, str._size);
std::swap(_capacity, str._capacity);
std::swap(_str, str._str);
}
c_str
const char* c_str()const
{
return _str;
}
访问字符串相关函数
operator[ ]
char& operator[](size_t idx)
{
assert(idx < _size);
return _str[idx];
}
const char& operator[](size_t idx)const
{
assert(idx < _size);
return _str[idx];
}
find和rfind
size_t find(const char* s, size_t pos = 0)
{
const char* ptr = strstr(_str + pos, s);
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr - _str;
}
}
size_t find(char ch)
{
for (size_t i = 0; i < _size; ++i)
{
if (ch == _str[i])
{
return i;
}
}
return npos;
}
//反向查找第一个匹配的字符
size_t rfind(char ch, size_t pos = npos)
{
string tmp(*this); //拷贝构造对象tmp
reverse(tmp.begin(), tmp.end()); //调用reverse逆置对象tmp的C字符串
if (pos >= _size) //所给pos大于字符串有效长度
{
pos = _size - 1; //重新设置pos为字符串最后一个字符的下标
}
pos = _size - 1 - pos; //将pos改为镜像对称后的位置
size_t ret = tmp.find(ch, pos); //复用find函数
if (ret != npos)
return _size - 1 - ret; //找到了,返回ret镜像对称后的位置
else
return npos; //没找到,返回npos
}
//反向查找第一个匹配的字符串
size_t rfind(const char* str, size_t pos = npos)
{
string tmp(*this); //拷贝构造对象tmp
reverse(tmp.begin(), tmp.end()); //调用reverse逆置对象tmp的C字符串
size_t len = strlen(str); //待查找的字符串的长度
char* arr = new char[len + 1]; //开辟arr字符串(用于拷贝str字符串)
strcpy(arr, str); //拷贝str给arr
size_t left = 0, right = len - 1; //设置左右指针
//逆置字符串arr
while (left < right)
{
::swap(arr[left], arr[right]);
left++;
right--;
}
if (pos >= _size) //所给pos大于字符串有效长度
{
pos = _size - 1; //重新设置pos为字符串最后一个字符的下标
}
pos = _size - 1 - pos; //将pos改为镜像对称后的位置
size_t ret = tmp.find(arr, pos); //复用find函数
delete[] arr; //销毁arr指向的空间,避免内存泄漏
if (ret != npos)
return _size - ret - len; //找到了,返回ret镜像对称后再调整的位置
else
return npos; //没找到,返回npos
}
关系运算符重载函数
//>运算符重载
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);
}
>>运算符的重载
//>>运算符的重载
istream& operator>>(istream& in, string& s)
{
s.clear(); //清空字符串
char ch = in.get(); //读取一个字符
while (ch != ' '&&ch != '\n') //当读取到的字符不是空格或'\n'的时候继续读取
{
s += ch; //将读取到的字符尾插到字符串后面
ch = in.get(); //继续读取字符
}
return in; //支持连续输入
}
<<运算符的重载
//<<运算符的重载
ostream& operator<<(ostream& out, const string& s)
{
//使用范围for遍历字符串并输出
for (auto e : s)
{
cout << e;
}
//cout<< s.c_str();//这种写法遇到\0结束,不能这么写
return out; //支持连续输出
}
getline
//读取一行含有空格的字符串
istream& getline(istream& in, string& s)
{
s.clear(); //清空字符串
char ch = in.get(); //读取一个字符
while (ch != '\n') //当读取到的字符不是'\n'的时候继续读取
{
s += ch; //将读取到的字符尾插到字符串后面
ch = in.get(); //继续读取字符
}
return in;
}