目录
一、为什么学习string类
C语言中,字符串是以'\0'结尾的一些字符的集合,为了方标操作,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
二、标准库中的string类
1、string类
·string是表示字符串的字符串类
·该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
·string在底层实际是:basic_string模板类的别名,typedef basic_string<char , char_traits , allocator>string;
·不能操作多字节或者变长字符的序列
2、string类的常用接口说明
(1)string类对象的常见构造
函数名称 | 功能说明 |
string() | 构造空的string类对象,即空字符串 |
string(const char* s) | 用C-string来构造string类对象 |
string(size_t n , char c) | string类对象中包含n个字符c |
string(const string& s) | 拷贝构造函数 |
void test()
{
string s1;
string s2("hello world");
string s3(s2);
}
(2)string类对象的容量操作
函数名称 | 功能说明 |
size | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty | 检测字符串是否为空串,是返回true,否返回false |
clear | 清空有效字符串 |
reserve | 为字符串预留空间 |
resize | 将有效字符的个数改成n个,多出的空间用字符c填充 |
注:
·size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
·clear()只是将string中有效字符清空,不改变底层空间大小。
·resize(size_t n)与resize(size_t n , char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n , char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
·reserve(size_t res_arg = 0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserve不会改变容量大小。
(3)string类对象的访问及遍历操作
函数名称 | 功能说明 |
push_back | 在字符串后尾插一个字符c |
append | 在字符串后追加一个字符串 |
operator+= | 在字符串后追加字符串str |
c_str | 返回C格式字符串 |
find+npos | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
(4)string类非成员函数
函数 | 功能说明 |
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> | 输入运算符重载 |
operator<< | 输出运算符重载 |
getline | 获取一行字符串 |
relational operator | 比较大小 |
三、string类的模拟实现
1、string类的成员描述
private:
size_t _size;
size_t _capacity;
char* _str;
public:
const static size_t npos = -1;
2、迭代器
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin()const
{
return _str;
}
const_iterator end()const
{
return _str + _size;
}
3、构造函数
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
4、拷贝构造
void Swap(string& tmp)
{
::swap(_str, tmp._str);
::swap(_size, tmp._size);
::swap(_capacity, tmp._capacity);
}
// s2(s1)
string(const string& s)
:_str(nullptr)
, _size(0)
, _capacity(0)
{
string tmp(s._str);
Swap(tmp); //this->swap(tmp);
}
5、赋值运算符重载
//s来顶替tmp,让s和this交换完成赋值
string& operator=(string s)
{
Swap(s);
return *this;
}
6、析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
7、c_str、size()和capacity()
const char* c_str()const
{
return _str;
}
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
8、[]的重载
const char& operator[](size_t pos)const
{
assert(pos < _size);
return _str[pos];
}
9、reserve的实现
void reverse(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
10、resize的实现
void resize(size_t n, char ch = '\0')
{
if (n > _size)
{
//插入数据
reverse(n);
for (size_t i = _size; i < n; i++)
{
_str[i] = ch;
}
_str[n] = '\0';
_size = n;
}
else
{
//删除数据
_str[n] = '\0';
_size = 0;
}
}
11、尾插push_back
void push_back(char ch)
{
/*if (_size == _capacity)
{
reverse(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';*/
insert(_size, ch);
}
注释掉的是正常实现,insert的实现会在下面写出。这是对insert的服用。
12、append的实现
//尾插字符串
void append(const char* s)
{
/*size_t len = strlen(s);
if (_size + len > _capacity)
{
reverse(_size + len);
}
strcpy(_str + _size, s);
_size += len;*/
insert(_size, s);
}
13、+=的重载
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
14、insert的实现
string& insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reverse(_capacity == 0 ? 4 : _capacity * 2);
}
size_t end = _size + 1;
while (end > pos)
{
_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 (_size + len > _capacity)
{
reverse(_size + len);
}
size_t end = _size + len;
while (end >= pos + len)
{
_str[end] = _str[end - len];
--end;
}
strncpy(_str + pos, str, len);
_size += len;
return *this;
}
15、erase的实现
void erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
if (pos == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
}
16、clear的实现
void clear()
{
_str[0] = '\0';
_size = 0;
}
17、find的实现
size_t find(const char* sub, size_t pos = 0)
{
assert(sub);
assert(pos < _size);
const char* ptr = strstr(_str + pos, sub);
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr - _str;
}
}
18、substr的实现
string substr(size_t pos, size_t len)const
{
assert(pos < _size);
size_t realLen = len;
if (len == npos || pos + len > _size)
{
realLen = _size - pos;
}
string sub;
for (size_t i = 0; i < realLen; ++i)
{
sub += _str[pos + i];
}
return sub;
}
19、逻辑判断符的重载
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);
}
20、流插入和流提取的重载
ostream& operator<<(ostream& out, const string& s)
{
for (size_t i = 0; i < s.size(); ++i)
{
out << s[i];
}
return out;
}
istream& operator>>(istream& in, string& s)
{
s.clear();
char ch;
ch = in.get();
const size_t N = 32;
char buff[N];
size_t i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == N - 1)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
buff[i] = '\0';
s += buff;
return in;
}