string类的常用接口
string类对象的常见构造
构造空的string类对象,即字符串:string()
用C语言的字符串来构造string类对象:string(const char* str)
用n个字符c来构造string类对象:string(size_t n, char c)
拷贝构造函数:string(const string&s)
区间构造:string(first,last),其中STL规定:所有的区间都是左闭右开[first,last)
函数名称 | 功能说明 |
string() | 构造空的string类 |
string(const char* str) | 用C语言字符串构造string类 |
string(size_t n, char c) | 用n个字符c构造string类 |
string(const string&s) | 拷贝构造函数 |
string(first,last) | 区间构造 |
void TestString1()
{
string s1;//空string类对象
string s2("hello");//用C字符串构造
string s3(6, 'a');//用5个字符a构造
string s4(s3);//拷贝构造
const char* str = "hello world";
string s5(str, str + 8);//区间构造
cout << s1 << ' ' << s2 << ' ' << s3 << ' ' << s4 << ' ' << s5 << endl;
//cin和cout支持string类对象的输入与输出
cin >> s1;
cout << s1 << endl;
}

string类的容量操作
函数名称 | 功能说明 |
size() | 返回字符串有效字符长度 |
length() | 返回字符串有效字符长度 |
capacity() | 返回空间总大小 |
empty() | 检测字符串是否为空串:空返回true,不空返回false |
clear() | 将有效元素清空(不修改空间总大小) |
reserve(size_t newcapacity) | 为字符串预留空间(修改capacity) |
resize(size_t n,char c) resize(size_t n) | 将有效元素个数修改到n个, 多余的字符用c(\0)填充 |
void TestString2()
{
string s1("hello string");
cout << s1.size() << endl;//返回字符串有效字符长度
cout << s1.length() << endl;//返回字符串有效字符长度
cout << s1.capacity() << endl;//返回空间总大小
if (s1.empty())//检测字符串是否为空:空返回true,不空返回false
{
cout << "s1 is empty" << endl;
}
else
{
cout << "s1 is not empty" << endl;
}
s1.clear();//将有效元素清空
cout << s1.size() << endl;
if (s1.empty())//检测字符串是否为空:空返回true,不空返回false
{
cout << "s1 is empty" << endl;
}
else
{
cout << "s1 is not empty" << endl;
}
}

reserve() 和resize() 详解
(1)reserve(size_t newcapacity):该函数扩容的是capacity(容量):
当newcapacity > oldcapacity 时将会进行扩容;
当newcapacity < oldcapacity && newcapacity >= 16时会忽略本次操作;
当newcapacity < 16 && 有效元素数量小于16时,会将空间大小缩小为15
👉为什么是缩小到15的原因:
在VS2022使用的STL版本是PJ版本:string对象内部包含了一个固定大小的数组:char buff[16]
绝大多数情况下的字符串中的有效元素个数都是小于16,这时直接使用固定大小的数组比从堆上开辟空间的效率更高;当有效元素的个数超过16个时,string类才需要真正从堆上开辟空间,开辟空间有时间消耗,所以string类从堆上开辟空间成功之后一般不在将空间缩小
void TestString3()
{
string s1("hello world");
//通过reserve()将s1底层空间扩大
//如果newcapacity > oldcapacity 将会扩容
s1.reserve(20);
s1.reserve(50);
s1.reserve(80);
s1.reserve(100);
s1.reserve(120);
//通过reserve()将s1底层空间缩小
//如果newcapacity < oldcapacity && newcapacity >= 16时
//reserve()会忽略此次操作,即不会缩小空间
s1.reserve(100);
s1.reserve(90);
s1.reserve(80);
s1.reserve(16);
//newcapacity < 16 && 有效元素数量小于16时,reserve()才会将空间缩小为15
s1.reserve(15);
s1.reserve(12);
}
newcapacity > oldcapacity :

newcapacity < oldcapacity && newcapacity >= 16:

(2)resize(size_t n,char c):
当扩大有效元素个数时不够的用字符c填充;
当n小于有效元素个数时将有效元素个数缩小到n个时不会改变capacity
void TestString4()
{
string s1("hello world");
//当扩大有效元素个数时不够的用字符填充
s1.resize(10, 'A');
s1.resize(20, 'B');
s1.resize(30, 'C');
//当n小于有效元素个数时将有效元素个数缩小到n个时不会改变capacity
s1.resize(30);
s1.resize(20);
s1.resize(17);
s1.resize(10);
s1.resize(5);
}
扩大有效元素个数:

缩小有效元素个数:

string类的访问和遍历操作
函数名称 | 功能说明 |
operator[] | 返回pos位置的字符,由const string类对象调用 |
begin() / end() | begin()获取的是第一个字符的迭代器,end()获取的是最后一个字符下一个位置的迭代器 |
rbegin() / rend() | 反向迭代器 |
范围for | 更简洁的遍历方式 |
operator[] 与 at() 越界访问时的报错区别
operator[]:当用下标访问符来访问元素时越界是assert断言出的问题

at():当用at()函数访问时越界是通过抛出out_of_range来进行报错的

迭代器:begin() 与 end()
begin():
返回的是首元素的位置
end():
返回的是最后一个有效元素的下一个位置
定义迭代器:string::iterator it = s1.begin();也可以使用 auto it = s1.begin();
迭代器使用:

void TestString13()
{
string s1("1234567890");
cout << s1 << endl;
reverse(s1.begin(), s1.end());
cout << s1 << endl;
sort(s1.begin(), s1.end());//默认排升序
cout << s1 << endl;
}
string类对象的修改操作
函数名称 | 功能说明 |
push_back() | 在字符串后尾插字符c(只能插入单个字符) |
operator+= | 在字符串后面追加字符串str |
append() | 在字符串后面追加一个字符串 |
c_str() | 返回C格式的字符串 |
find() / npos | 从pos的位置开始查找,返回第一次查找到的字符,如果没有找到则返回npos |
rfind() | 从pos的位置开始反向查找,返回第一次查找到的字符,如果没有找到则返回npos |
substr() | 从pos位置开始截取子串 |
insert() | 在任意位置插入 |
erase() | 在任意位置删除 |
push_back() / operator+=()

void TestString6()
{
string s1("hello string");
//在字符串后面尾插字符c
s1.push_back('!');
cout << s1 << endl;
//拼接c格式的字符串
s1 += "!!!!!";
cout << s1 << endl;
//拼接string类对象
string s2(" hello world");
s1 += s2;
cout << s1 << endl;
}
append():

void TestString7()
{
string s1("hello");
//string& append (size_t n, char c);
//在s1后尾插1个字符' '
s1.append(1, ' ');//s1.push_back(' ') || s += ' ';
cout << s1 << endl;
//string& append (const char* s);
//在s1后追加字符串" world"
s1.append("world");//s1 += " world";
cout << s1 << endl;
//string& append(const string & str);
//在s1后面拼接s2(string类对象)
string s2(" string");
s1.append(s2);
cout << s1 << endl;
//string& append (const string& str, size_t subpos, size_t sublen);
//在s1对象后面拼接s3对象从第2个位置开始,拼接长度为3
string s3("1234567");
s1.append(s3, 2, 3);
cout << s1 << endl;
}
insert():

void TestString8()
{
string s1("hello");
//string& insert(size_t pos, size_t n, char c);
//在0号位置插入3个字符A
s1.insert(0,3, 'A');
cout << s1 << endl;
//string& insert (size_t pos, const char* s);
//在0号位置插入C格式的字符串
s1.insert(0, "string");
cout << s1 << endl;
//string& insert (size_t pos, const string& str);
//在13号位置之后,在14号位置之前,插入string类对象s2
string s2("HELLO");
s1.insert(14, s2);
cout << s1 << endl;
//void insert (iterator p, InputIterator first, InputIterator last);
//在s1对象的0号位置之前插入s3对象的一段区间(0号位置到最后一个位置)
string s3("BBBB");
s1.insert(s1.begin(), s3.begin(), s3.end());
cout << s1 << endl;
}
erase():

void TestString9()
{
string s1("hello string");
//string& erase(size_t pos = 0, size_t len = npos);
//删除s1对象的0号位置到6号位置
s1.erase(0, 6);//删除“hello ”
cout << s1 << endl;
//删除从s1对象的0号位置开始,在向后加2的位置
s1.erase(s1.begin() + 2);//删除“r”
cout << s1 << endl;
//删除s1对象的一个区间[s1.begin(),s1.end())
s1.erase(s1.begin(), s1.end());//删除所有
cout << s1 << endl;
}
c_str():

void TestString14()
{
string s1("12345");
//c_str():返回C格式的字符串const char*
int ret = atoi(s1.c_str());
cout << ret << endl;
}
find():

void TestString15()
{
string s1("123142153115");
size_t pos = 0;
//找到所有的1将其替换成0
while (true)
{
//size_t find (char c, size_t pos = 0) const;
//从pos位置开始查找字符'1',返回第一次找到的位置
pos = s1.find('1',pos);
if (pos == string::npos)
break;
s1[pos] = '0';
pos += 1;
}
cout << s1 << endl;
}
rfind()、substr()

void TestString16()
{
//获取文件最后一个点之后的内容
string s1("123.txt.cpp");
size_t pos = s1.rfind(".") + 1;
//string substr (size_t pos = 0, size_t len = npos) const;
//从pos位置开始截取n个字符,n没有传递表示从开始截取到末尾
string postfix = s1.substr(pos);
cout << postfix << endl;
}
在VS2022下string类的扩容机制
在vs2022版本下,string类的以1.5倍的方式扩容的,vs2022是PJ版本

void TestString10()
{
string s1;
size_t cap = 0;
for (size_t i = 0; i < 100; i++)
{
s1.push_back('A');
if (cap != s1.capacity())
{
cap = s1.capacity();
cout << cap << endl;
}
}
}
在linux下:SGI版本STL的string是按照2倍的方式扩容的
范围for:更简洁的遍历方式

void TestString11()
{
string s1("1234567890");
for (auto e : s1)//范围for
{
cout << e;
}
cout << endl;
}
auto :auto是一个占位符,系统可以根据后面的值来推测出用auto定义的变量的类型
使用条件: 1.for循环的范围必须是确定的
2.迭代的对象需要实现++和==的操作
reverse():反转字符串函数
void reverse(iterator begin(),iterator end());

void TestString12()
{
string s1("hello string");
cout << s1 << endl;
reverse(s1.begin(), s1.end());
cout << s1 << endl;
}
string类的非成员函数
函数名称 | 功能说明 |
getline() | 获取一行字符串 |
operator+ | 尽量少用,因为传值返回导致深拷贝效率低 |
relational operator | 大小比较 |
getline():
cin是以空白字符来进行分割的,cin不能接收空白字符

getline()是接收一行的字符串

string类大小比较
从最高位开始比,ASCII码更大的字符串更大,如果相等,比次高位,以此类推。
所以在string中9>89,因为最高位9>8;如果每个数字都相等,位数更大的显然更大,例如:
99999>9999。