目录
[relational operators](#relational operators)
string类简介
C++ 的 std::string 是标准库提供的字符串类,使用时需要引入头文件<string>。它封装了字符序列,能够自动管理内存,相比 C 语言的字符数组,使用更加简便安全,内置了多种成员函数完成字符串的获取、查找、修改与截取等操作,支持运算符重载,方便字符串的拼接和比较。
https://legacy.cplusplus.com/reference/string/string/?kw=string 可通过该网站了解

总结:
std::string 是 C++ 标准库中的字符串类,本质是 basic_string<char>的类型别名。字符串是用来表示字符序列的对象,标准 string 类为这类对象提供支持,它的接口类似于字节标准容器,同时增加了专门用于操作单字节字符字符串的特性。
cpp
typedef basic_string<char, char_traits, allocator>string;
string 类是 basic_string 类模板的实例化版本,以 char 作为字符类型,并使用默认的字符特征与内存分配器类型。
需要注意的是,这个类以字节为单位处理数据,和字符编码相互独立。如果使用它处理 UTF-8 这类多字节、变长字符序列,类内所有成员函数以及迭代器,都只会按照字节进行操作,而不是按照编码后的实际字符。
string类常用接口(成员函数)
string类对象默认成员函数
string类对象构造函数
这张图片介绍 C++ 中 std::string 的各类构造函数,也就是创建 string 对象的多种方式。列出了 string 全部重载构造函数,用于构造 string 对象,不同版本对应不同的初始化方法。

第一种是默认构造函数,生成一个长度为 0 的空字符串。
第二种是拷贝构造函数,新建一个字符串,完整复制另一个 string 对象 str 的内容。
第三种是子串构造函数,从 str 的 pos 位置开始,取出 len 个字符生成新字符串,如果 len 省略,默认读到字符串末尾。
第四种从 C 语言字符串构造,复制以空字符结尾的字符数组。第五种从缓冲区构造,从字符数组 s 里复制前 n 个字符。
第六种填充构造函数,创建由 n 个连续字符 c 组成的字符串。
第七种范围构造函数,利用输入迭代器区间 first,last 内的字符创建字符串。
第八种初始化列表构造函数,是 C++11 新增的特性,直接读取初始化列表中的字符。
第九种移动构造函数,直接接管原字符串 str 的内容,原对象还保持合法状态,但内容不再确定。
参数解释如下:

这张图是对构造函数里各个参数的说明。
str 是源字符串对象,用来被复制或者转移内容。
pos 是子串的起始下标,string 下标从 0 开始,如果 pos 超过原字符串长度,会抛出 out_of_range 异常。
len 代表要截取的子串长度,当它取值为 string::npos 时,代表一直读取到字符串末尾,如果原字符串剩余字符不足,就尽可能复制所有剩下字符。
s 是字符数组的指针,可以是 C 风格字符串。
n 是需要复制的字符个数。
c 是用来填充字符串的目标字符,新字符串全部由这个字符组成。
first 和 last 是一对输入迭代器,区间是左闭右开,包含 first 指向元素,不包含 last 指向元素。
il 是初始化列表对象,由初始化列表语法自动生成。size_t 是无符号整数类型,是这些位置、长度参数的类型。
简单来说,第一张图展示构造函数的重载列表与功能简述,说明创建 string 对象的多种途径,第二张图解释各个参数的含义、约束以及使用时的注意事项。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s1;
string s2("hello world");
string s3(5, 'x');
string s4(s2);
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
cout << s4 << endl;
return 0;
}
这段代码演示了 std::string 中四种常用的构造函数。
- s1 调用默认构造函数,生成空字符串。
- s2 使用 C 语言字符串常量完成构造,内容为 hello world。
- s3 调用填充构造函数,生成由 5 个 x 组成的字符串。
- s4 调用拷贝构造函数,复制 s2 里面的全部内容。随后依次打印这四个对象,s1 为空,不会输出可见字符,s2 输出 hello world,s3 输出 xxxxx,s4 输出 hello world。
| 函数名称 | 功能说明 |
|---|---|
| string() | 构造空的string类对象,即空字符串 |
| string(const char* s) | 用C-string来构造string类对象 |
| string(size_t n,char c) | string类对象中包含n个字符c |
| string(const string&s) | 拷贝构造函数 |
string类对象析构函数
如图:

这张图片介绍的是 std::string 的析构函数~string。析构函数的作用是销毁 string 对象,它会借助分配器释放 string 申请的全部存储空间。析构函数具备不抛出异常的保证,执行过程不会抛出异常。
在程序运行过程中,当 string 对象离开作用域时,析构函数会被自动调用,它会自动释放字符串底层动态开辟的字符数组内存,避免内存泄漏。结合前面的构造函数来看,构造函数负责创建对象并分配内存,析构函数负责销毁对象并回收内存,二者相互配合完成 string 对象生命周期的管理。
string类对象赋值运算符重载函数
如图:

std::string 的赋值运算符 operator=,它的作用是给字符串赋予新内容,替换掉对象原本存储的数据,更多赋值方式可以查看 assign 成员函数。
这个运算符存在多个重载版本
- 第一种接收 const string & 类型的对象 str,复制另一个 string 对象的内容;
- 第二种接收 const char * 类型的 C 风格字符串,复制以空字符结尾的字符序列;
- 第三种接收单个字符 c,会让字符串只保存这一个字符,长度变为 1。
C++11 还增加了移动赋值以及初始化列表赋值的重载版本。
参数 str 代表源字符串对象,如果是移动赋值,原对象内容会转移,原对象保持合法但状态不确定。
参数 s 是指向 C 风格字符串的指针,函数会复制该字符序列作为字符串新内容。
参数 c 是单个字符,赋值后字符串仅包含这一个字符。
参数 il 是初始化列表对象,程序会按顺序复制列表内所有字符完成赋值。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
//构造空的string类对象,即空字符串
string s1;
//用C-string来构造string类对象,内容为hello world
string s2("hello world");
//string类对象中包含5个字符'x'
string s3(5, 'x');
//拷贝构造函数,用s2拷贝生成s4
string s4(s2);
//输出s4当前的内容
cout << s4 << endl;
//调用赋值运算符重载,将s3赋值给s4,覆盖原有内容
s4 = s3;
//输出赋值后的s4
cout << s4 << endl;
//调用赋值运算符重载,将C风格字符串"ABCDEF"赋值给s4
s4 = "ABCDEF";
//输出赋值后的s4
cout << s4 << endl;
//调用赋值运算符重载,将单个字符'Y'赋值给s4
s4 = 'Y';
//输出赋值后的s4
cout << s4 << endl;
return 0;
}
string类对象的迭代遍历及元素访问操作
共八个迭代器访问接口:

共4个元素访问接口:

| 函数名称 | 功能说明 |
|---|---|
| operator\[\] | 返回pos位置的字符,const string类对象调用 |
| begin+end | begin获取一个字符的迭代器+end获取最后一个字符下一个位置的迭代器 |
| rbegin+rend | begin获取一个字符的迭代器+end获取最后一个字符下一个位置的迭代器 |
| 范围for | C++支持更简洁的范围for的新遍历方式 |
operator\[\]运算符重载

std::string 的下标运算符 operator \[\],用来获取字符串指定位置上的字符,一共有两个重载版本,普通版本返回字符的引用,常量版本返回常量字符引用。该运算符会返回字符串 pos 位置字符的引用,string 的下标从 0 开始计数,第一个字符对应的下标是 0。
当字符串是 const 修饰的常量对象,并且 pos 等于字符串长度时,会返回空字符 '\0' 的引用。参数 pos 代表字符在字符串中的位置,类型是无符号整数 size_t。返回值根据对象是否为常量区分,如果 string 对象带有 const 修饰,返回 const char&,普通对象则返回 char&,可以通过这个引用修改对应位置的字符。
需要注意,operator \[\] 不会做下标越界检查,如果 pos 超出有效范围,会产生未定义行为。
用operator\[\]遍历string类对象:
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string st1("Hello world");
for (size_t i = 0; i < st1.size(); i++)
{
cout << st1[i] << endl;
}
return 0;
}
用operator\[\]修改string类对象:
cpp
int main()
{
string st1("Hello world");
for (size_t i = 0; i < st1.size(); i++)
{
++st1[i];
cout << st1[i] << endl;
}
return 0;
}
顺序迭代器begin+end
begin:

std::string 的 begin 成员函数,作用是返回指向字符串起始位置的迭代器。
它包含两个重载版本,普通对象调用返回 iterator,const 修饰的 string 对象调用返回 const_iterator。这个函数不需要传入任何参数,返回的迭代器指向字符串的第一个字符。如果 string 对象带有 const 修饰,返回的是 const_iterator,只能读取字符,不能修改;普通 string 对象返回 iterator,可以读写字符。string 的 iterator 与 const_iterator 都属于随机访问迭代器,iterator 指向普通字符,const_iterator 指向常量字符。
注意:
const_iterator 是迭代器的类型,代表常量迭代器。这个迭代器本身可以移动,比如可以自增、自减,但是不能通过这个迭代器修改它指向的字符。也就是说,*it 不能赋值,只能读取内容。当 string 对象是 const 常量字符串的时候,begin () 返回的就是 const_iterator。
const iterator 是给普通迭代器 iterator 加上 const 修饰。此时迭代器本身不能改变指向,迭代器的位置固定,不能移动,但是可以通过这个迭代器修改指向的字符。简单来说,迭代器本身不可变,指向的数据可以修改。
cpp#include<iostream> #include<string> using namespace std; int main() { string s = "hello"; // const_iterator:迭代器可移动,不能修改指向内容 string::const_iterator cit = s.begin(); cit++; //*cit = 'x'; 这一行编译报错,不能修改 // const iterator:迭代器本身不可移动,指向内容可以修改 const string::iterator it = s.begin(); //it++; 这一行编译报错,迭代器不能移动 *it = 'H'; cout << s << endl; return 0; }
end:

std::string 的 end 成员函数,用来返回指向字符串末尾之后位置的迭代器。
它有两个重载版本,普通 string 对象调用返回 iterator,const 修饰的 string 对象调用返回 const_iterator。这个迭代器指向字符串最后一个字符的下一个位置,这是一个理论上的虚拟位置,不可以对这个迭代器解引用读取字符。
标准库采用左闭右开区间规则,end 经常和 begin 搭配使用,用来划定包含字符串全部字符的范围。如果 string 是空字符串,end 返回的迭代器和 begin 返回的迭代器相等。这个函数不需要传入参数。返回值是字符串末尾之后位置的迭代器,如果 string 对象带有 const 修饰,返回 const_iterator,普通对象返回 iterator,iterator 和 const_iterator 都属于随机访问迭代器。
begin+end迭代器顺序遍历访问string类对象:
cpp
int main()
{
string st1("Hello world");
//迭代器
string::iterator it = st1.begin();
while (it != st1.end())
{
cout << *it << endl;
++it;
}
return 0;
}
begin+end迭代器修改string对象:
cpp
int main()
{
string st1("Hello world");
//迭代器
string::iterator it = st1.begin();
while (it != st1.end())
{
++(*it);
cout << *it << endl;
++it;
}
return 0;
}
反向迭代器rbegin+rend
如图:

std::string 的 rbegin 反向起始迭代器函数。它有两个重载版本,普通 string 对象调用返回 reverse_iterator,const 修饰的 string 对象调用返回 const_reverse_iterator。
rbegin 会返回一个反向迭代器,指向字符串的最后一个字符,也就是反向遍历的起点。反向迭代器的遍历方向是反过来的,对反向迭代器做自增操作,迭代器会向字符串开头方向移动。**rbegin 指向的位置,正好是 end 所指向位置的前一个字符。**这个函数不需要传入参数。返回值是字符串反向起点的反向迭代器,如果 string 对象带有 const 修饰,返回 const_reverse_iterator,普通对象返回 reverse_iterator。
reverse_iterator 和 const_reverse_iterator 都属于反向随机访问迭代器,reverse_iterator 可以修改指向字符,const_reverse_iterator 只能读取字符。一般 rbegin 会和 rend 配合使用,用来反向遍历字符串。

std::string 的 rend 反向结束迭代器函数。它有两个重载版本,普通 string 对象调用返回 reverse_iterator,const 修饰的 string 对象调用返回 const_reverse_iterator。
rend 返回的反向迭代器指向字符串第一个字符前面的虚拟位置,也就是反向遍历的终点,这个位置不存在真实字符,不能解引用。rbegin 到 rend 构成的区间包含字符串全部字符,遍历顺序是逆序。这个函数不需要传入参数。返回值是反向终点的反向迭代器,如果 string 对象带有 const 修饰,返回 const_reverse_iterator,普通对象返回 reverse_iterator。reverse_iterator 和 const_reverse_iterator 都属于反向随机访问迭代器。
rbegin 和 rend 搭配做反向遍历,区间同样是左闭右开。rbegin 是反向起点,rend 是反向终点。
使用rbegin+rend迭代器反向遍历访问string类对象:
cpp
int main()
{
string st1("Hello world");
//反向迭代器
string::reverse_iterator rit = st1.rbegin();
while (rit != st1.rend())
{
cout << *rit << endl;
++rit;
}
return 0;
}
使用rbegin+rend反向迭代器修改string类对象:
cpp
int main()
{
string st1("Hello world");
//反向迭代器
string::reverse_iterator rit = st1.rbegin();
while (rit != st1.rend())
{
++(*rit);
cout << *rit << endl;
++rit;
}
return 0;
}
范围for
范围 for,全称:range-based for loop。它本质是语法糖,底层会自动调用容器的 begin 和 end 迭代器来完成遍历,省去手动写迭代器的代码。
一共有三种常用写法,各自作用不一样。第一种是值拷贝写法,char ch : s。每次循环会把字符串里的字符复制一份放到 ch 里面,ch 只是副本,修改 ch 不会改变原来字符串里的内容。这种写法适合简单读取,char 类型拷贝开销很小,但是如果是大型对象,拷贝会带来额外性能消耗。
cpp
string s = "abc";
for(char ch : s)
{
ch = 'x'; // 修改的只是副本,原s不变
cout << ch;
}
第二种是普通引用写法**,char& ch : s。**ch 是原字符串字符的别名,不会产生拷贝,直接绑定原始数据。修改 ch 就会直接修改原字符串对应的字符。这个写法只能用于非 const 的 string 对象,如果字符串被 const 修饰,不能使用普通引用。
cpp
string s = "abc";
for(char& ch : s)
{
ch += 1; // 直接修改原字符串s
}
第三种是 const 引用写法,const char& ch : s。ch 是常量引用,绑定原始字符,不会拷贝,同时不能通过 ch 修改字符内容。这种写法没有拷贝开销,效率高。const 修饰的字符串只能用这个写法,普通字符串只读遍历的时候也推荐使用,避免不小心修改原数据。
cpp
const string s = "abc";
for(const char& ch : s)
{
cout << ch;
// ch = '1'; 编译报错,禁止修改
}
编译器底层翻译逻辑,当你写范围 for 遍历 string 的时候,编译器会自动转换成迭代器版本的代码。循环开始的时候只会获取一次 begin 迭代器和 end 迭代器,之后循环就依靠这两个迭代器完成遍历。这就带来一个重要的限制,在范围 for 循环内部,不要对 string 做插入或者删除字符的操作,一旦字符串扩容或者长度改变,迭代器就会失效,程序会出现未定义行为,可能乱码或者崩溃。
cpp
string s = "hello";
for (char ch : s)
{
cout << ch;
}
改代码转变为:
cpp
auto __begin = s.begin();
auto __end = s.end();
for( ; __begin != __end; ++__begin )
{
char ch = *__begin;
cout << ch;
}
范围 for 有几个明显的限制。
第一,它只能正向遍历,不能直接反向遍历字符串,如果想要逆序访问字符,还是要用 rbegin 和 rend 反向迭代器。
第二,范围 for 只能拿到元素本身,不会提供当前元素的下标,如果遍历过程中需要知道索引位置,范围 for 就不太方便,这时优先选用下标或者迭代器遍历。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s = "hello";
// 1. 值拷贝:副本,修改不影响原字符串
cout << "值拷贝遍历:";
for(char ch : s)
{
// ch = 'a'; // 无效,副本
cout << ch << " ";
}
cout << endl;
// 2. 普通引用:可以修改原字符串
cout << "引用遍历(修改原串):";
for(char& ch : s)
{
ch -= 32; //小写转大写
cout << ch << " ";
}
cout << endl;
//3.const引用:只读,推荐只读场景
const string str = "TEST";
cout << "const引用只读遍历:";
for(const char& ch : str)
{
cout << ch << " ";
//ch = 'X'; //报错,不能修改
}
cout << endl;
return 0;
}
范围for顺序访问string类对象:
cpp
int main()
{
string st1("Hello world");
for (auto ch : st1)
{
cout << ch << endl;
}
return 0;
}
范围for修改string类对象:
cpp
int main()
{
string st1("Hello world");
for (auto &ch : st1)
{
ch++;
cout << ch << endl;
}
return 0;
}
string类对象的容量操作
string类对象容量相关操作函数共有9个,如下:

| 函数名称 | 功能说明 |
|---|---|
| size | 返回字符串有效字符长度 |
| length | 返回字符串有效字符长度 |
| capacity | 返回空间总大小 |
| empty | 检测字符串是否为空串,是返回true,否则返回false |
| clear | 清空有效字符 |
| reserve | 为字符串预留空间 |
| resize | 将有效字符的个数改成n个,多出的空间用字符c填充 |
size()函数
如图:

std::string::size 是 string 的成员函数,函数原型为 size_t size () const,不需要参数,是 const 成员函数,const 对象也可以调用。它返回字符串占用的字节数量,也就是字符串里面有效字符的个数,返回值类型是 size_t,这是一种无符号整型。size 统计的是字节数,不是字符个数,在处理 UTF-8 这类多字节编码文字时,一个汉字占多个字节,size 得到的结果会大于文字数量。size 和 length 是完全等价的同义词,两者返回的值一模一样。size 代表当前有效字符的字节数,和 capacity 容量不是一回事,capacity 是预先分配的内存总大小,capacity>= size。
重点要注意返回类型 size_t 是无符号数,不能直接和负数比较,容易出现逻辑错误。如果写 if (s.size () - 10 > 0),当 s.size () 小于 10 的时候,无符号数减负数会变成一个很大的正数,判断永远成立,引发 bug。
cpp
int main()
{
string st1("Hello world");
cout << st1.size() << endl;
return 0;
}
length()函数
如图:

std::string::length 是 string 的成员函数,原型为 size_type length () const,无参数,属于 const 成员函数,const 字符串对象也可以调用。它返回字符串中有效字符的数量,返回类型 size_type 本质就是无符号整型 size_t。
文档明确说明 length 和 size 是同义词,两者返回完全相同的值,底层实现没有区别,只是名字不同。**这里统计的是 string 内部存储的 char 单元数量,也就是字节数,不是语义上的文字个数。**如果存放 UTF-8 中文,一个汉字占用多个 char 字节,length 返回的数值会大于汉字数量。length 得到的是当前有效字符数量,不等于底层预分配的内存容量 capacity,capacity 是预先申请好的内存总大小,capacity 一定大于或者等于 length。
cpp
int main()
{
string st1("Hello world");
cout << st1.length() << endl;
return 0;
}
capacity()函数
如图:

std::string::capacity 是 string 的成员函数,原型为 size_type capacity () const,无参数,属于 const 成员函数,const 对象也可以调用。它返回当前底层已经分配好的内存空间大小,单位是 char。capacity 代表预先申请的内存总量,而 size 或者 length 是当前字符串里面有效字符的数量,capacity 大于等于 size。多出来的预留空间,是为了后续追加字符的时候减少内存重新分配的开销。
capacity 并不是字符串长度的硬上限,当写入字符超出 capacity 时,string 会自动重新分配更大的内存。字符串理论上能存放的最大长度由 max_size 决定。string 的 capacity 在修改字符串时有可能发生变化。可以调用 reserve 函数手动修改 capacity,主动提前预留内存。返回类型 size_type 是无符号整型。
cpp
int main()
{
string st1("Hello world");
cout << st1.size() << endl;
cout << st1.capacity() << endl;
return 0;
}
empyt()函数
如图:

std::string::empty 是 string 的成员函数,原型为 bool empty () const,无参数,属于 const 成员函数,const 对象也可以调用。它用来判断字符串是否为空,判断标准就是字符串长度是否等于 0。当字符串有效长度为 0 时返回 true,有字符则返回 false。这个函数不会修改字符串本身内容,如果想要清空字符串,需要调用 clear 函数。
cpp
int main()
{
string st1("Hello world");
string st2;
cout << st1.empty() << endl;
cout << st2.empty() << endl;
return 0;
}
clear()函数
如图:

std::string::clear 是 string 的成员函数,原型为 void clear (),无参数,无返回值。它的作用是删除字符串内部全部内容,执行之后字符串长度变成 0,变成空串。调用 clear 只会修改 size,不会释放底层已经分配的内存,capacity 保持不变 。
cpp
int main()
{
string st1("Hello world");
cout << st1.empty() << endl;
st1.clear();
cout << st1.empty() << endl;
return 0;
}
reserve()函数
如图:

std::string::reserve 是 string 的成员函数,原型为 void reserve (size_type n = 0),无返回值。它用来主动请求修改 string 的 capacity 预留内存容量。
如果传入的 n 大于当前 capacity,函数会重新分配内存,将 capacity 扩充到 n 或者更大的值。如果 n 小于等于当前 capacity,这只是一个不强制生效的缩减请求,编译器可以选择不缩减容量,capacity 有可能保持不变。
reserve 不会改变 string 的 size,也不会修改字符串里面的内容,只作用于底层预分配内存。参数 n 是无符号整型 size_type。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s = "abc";
cout << "初始size:" << s.size() << " capacity:" << s.capacity() << endl;
s.reserve(100);
cout << "reserve(100)后size:" << s.size() << " capacity:" << s.capacity() << endl;
s.reserve(10);
cout << "reserve(10)后size:" << s.size() << " capacity:" << s.capacity() << endl;
return 0;
}
resize()函数
如图:

std::string::resize 用来修改字符串的有效长度 size,有两个重载版本。
第一个版本只传入 n,当 n 小于当前长度,截断字符串只保留前 n 个字符;当 n 大于当前长度,末尾补空字符 '\0'。
第二个版本额外传入字符 c,扩容新增位置就用 c 填充。resize 会改变 size,有可能触发 capacity 扩容。resize 修改的是有效字符数量,和 reserve 只修改预留容量不改变 size 有本质区别。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s = "hello";
cout << "原字符串:" << s << " size:" << s.size() << endl;
// 1.缩短长度,n小于原size
s.resize(3);
cout << "resize(3)后字符串:" << s << " size:" << s.size() << endl;
// 2.拉长长度,不指定填充字符,默认填充'\0'
s.resize(6);
cout << "resize(6)后size:" << s.size() << endl;
// 3.拉长长度,指定填充字符'x'
s.resize(10, 'x');
cout << "resize(10,'x')后字符串:" << s << " size:" << s.size() << endl;
return 0;
}
注意:
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 不会改变容量大小。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s = "hello";
cout << "size:" << s.size() << " length:" << s.length() << endl;
s.clear();
cout << "clear后 size:" << s.size() << " capacity:" << s.capacity() << endl;
s = "hello";
s.resize(3);
cout << "resize(3) 缩短后:" << s << " capacity:" << s.capacity() << endl;
s.resize(8);
cout << "resize(8) 扩容,默认补\\0, size:" << s.size() << endl;
s.resize(12, 'a');
cout << "resize(12,'a')扩容,用a填充:" << s << " size:" << s.size() << endl;
s.reserve(30);
cout << "reserve(30)后 capacity:" << s.capacity() << " size:" << s.size() << endl;
s.reserve(10);
cout << "reserve(10),参数小于当前capacity,容量不变:" << s.capacity() << endl;
return 0;
}
string类对象的字符串修改操作
修饰符:

字符串操作:

| 函数名称 | 功能说明 |
|---|---|
| push_back | 在字符串后尾插字符c |
| append | 在字符串后追加一个字符串 |
| operator+= | 在字符串后追加字符串str |
| c_str | 返回C格式字符串 |
| find+nops | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
| rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
| substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
push_back()函数
如图:

std::string::push_back 函数原型为 void push_back (charT c),无返回值。功能是在 string 尾部追加一个字符,字符串长度 size 加 1。该接口只能追加单个字符 ,不能直接追加字符串。如果底层空间不足,会自动扩容。它和 += 追加字符效果类似,是容器风格的接口,和 vector 的 push_back 保持接口统一。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s = "test";
cout << "原始字符串:" << s << " size:" << s.size() << endl;
// push_back:在字符串末尾追加单个字符,只能传字符,不能传字符串
s.push_back('!');
cout << "push_back('!')后:" << s << " size:" << s.size() << endl;
s.push_back('A');
cout << "push_back('A')后:" << s << " size:" << s.size() << endl;
return 0;
}
append()函数
如图:

std::string::append 有多个重载版本,作用是在字符串末尾追加字符序列,返回自身引用,支持链式调用。
- 接收 string 对象,直接追加整个字符串;
- 接收 string + 起始下标 + 长度,追加该 string 的一段子串;
- 接收 C 语言 const char * 字符串,追加完整 C 串;
- 接收 const char* + n,只取字符数组前 n 个字符追加;
- 接收数量 n 和字符 c,追加 n 个 c 字符;
- 迭代器区间版本,追加 [first,last) 范围内字符;
- 初始化列表版本,追加多个字符。 append 相比 push_back 功能更强,支持一次性追加多个字符,push_back 仅能追加单个字符。operator+= 也可以追加,append 重载更多,适合复杂追加场景。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s = "hello";
string str2 = "world";
cout << "原始字符串s:" << s << endl;
// 1.追加完整string对象
s.append(str2);
cout << "append(str2):" << s << endl;
// 2.追加str2的子串,从下标0取3个字符
s.append(str2,0,3);
cout << "append(str2,0,3):" << s << endl;
// 3.追加C风格字符串
s.append("!!!");
cout << "append(\"!!!\"):" << s << endl;
// 4.追加C字符串前2个字符
s.append("abcd",2);
cout << "append(\"abcd\",2):" << s << endl;
// 5.追加5个相同字符'*'
s.append(5,'*');
cout << "append(5,'*'):" << s << endl;
return 0;
}

append 参数说明:
- str 是同类型的 string 对象,作为追加的源字符串。
- subpos 是源字符串 str 中开始截取的下标,下标从 0 开始。如果 subpos 超过 str 的长度,会抛出 out_of_range 异常。
- sublen 是要截取追加的字符个数;如果设置为 string::npos,代表直接截取到 str 末尾;如果剩余字符不足,则有多少复制多少。
- s 是字符数组指针,C 风格字符串。
- n 是需要拷贝的字符数量。
- c 是需要重复追加的单个字符。
- first,last 是迭代器区间 [first, last),左闭右开区间,把区间内字符追加进来。
- il 是初始化列表,支持一次性传入多个字符。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s = "hello";
string str = "teststring";
cout << "原始s:" << s << endl;
// append(str, subpos, sublen):从str的subpos位置开始,截取sublen个字符追加
s.append(str, 4, 3);
cout << "append(str,4,3):" << s << endl;
// sublen传string::npos代表取到str末尾
s.append(str, 2, string::npos);
cout << "append(str,2,npos):" << s << endl;
// append(n,c) 追加n个字符c
s.append(3, '!');
cout << "append(3,'!'):" << s << endl;
return 0;
}
operator+=()函数
如图:

std::string::operator+= 有多个重载版本,作用是在字符串末尾追加内容,返回当前对象的引用。
str 是同类型的 string 对象,会把这个字符串整体追加到末尾。
s 是以 '\0' 结尾的 C 风格字符指针,将 C 字符串追加到末尾,长度由末尾结束符自动判定。
c 是单个字符,追加到字符串末尾。
il 是初始化列表,可以一次性追加多个字符。
operator+= 本质上内部调用 append 实现,语法更加简洁,适合日常简单追加场景。
如果需要截取子串、指定数量追加等复杂操作,需要使用 append。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s = "hello";
string str = " world";
cout << "原始s:" << s << endl;
// 追加string对象
s += str;
cout << "s += str:" << s << endl;
// 追加C风格字符串
s += "!!";
cout << "s += \"!!\":" << s << endl;
// 追加单个字符
s += 'A';
cout << "s += 'A':" << s << endl;
return 0;
}
c_str()函数
如图:

std::string::c_str 函数原型为 const charT* c_str () const,无参数。 它返回一个字符数组指针,数组里存放 string 对应的字符序列,并且在末尾自动添加 '\0' 结束符,得到标准 C 风格字符串。 返回的指针指向的内容不允许修改。如果后续调用修改该 string 对象的成员函数,这个指针就会失效。 该函数主要用于 C++ 的 string 和 C 语言字符串接口交互。C++11 之后 c_str () 和 data () 效果基本一致。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s = "hello world";
const char* p = s.c_str();
cout << p << endl;
// 可以传入接收C字符串的函数
printf("%s\n", p);
return 0;
}
find+nops函数
如图:


std::string::find 用于在字符串中查找目标序列第一次出现的位置,返回值类型为 size_type。pos 参数默认等于 0,代表从下标 0 开始查找;如果指定 pos,则只从 pos 位置(包含 pos)向后搜索,忽略 pos 之前的内容。find 要求完整序列匹配,和 find_first_of 不同,查找多字符目标时必须全部字符连续匹配才算找到,不能只匹配单个字符。
重载版本介绍:
- 接收 string 对象 str,pos 默认 0,查找 str 完整子串第一次出现位置。
- 接收 C 风格字符串 s,pos 默认 0,查找 C 字符串。
- 接收字符指针 s、pos、n,取 s 的前 n 个字符作为目标序列查找。
- 接收单个字符 c,pos 默认 0,查找该字符第一次出现位置。 查找失败时返回 string::npos。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s = "abcabc123";
size_t ret;
ret = s.find("abc", 1);
if(ret != string::npos)
cout << ret << endl;
ret = s.find("abx");
if(ret == string::npos)
cout << "未找到" << endl;
return 0;
}
rfind()函数
如图:

std::string::rfind 用于在字符串中查找目标序列最后一次出现的位置,返回值类型为 size_type。rfind 是从后往前查找,pos 参数默认值为 string::npos。当指定 pos 时,只查找起始位置小于等于 pos 的匹配,忽略 pos 之后的所有匹配。和 find 一样,查找多字符目标时需要完整连续序列匹配,不是匹配单个字符。 重载版本介绍:
- 接收 string 对象 str,pos 默认 npos,查找 str 子串最后一次出现位置。
- 接收 C 风格字符串 s,pos 默认 npos,查找 C 字符串。
- 接收字符指针 s、pos、n,取 s 的前 n 个字符作为目标序列查找。
- 接收单个字符 c,pos 默认 npos,查找该字符最后一次出现位置。 查找失败时返回 string::npos。
参数说明:
str 是待搜索的目标 string 字符串。 pos 是查找的上限下标,匹配的子串起始位置不能超过 pos。pos 大于等于字符串长度时,搜索整个字符串。下标从 0 开始。 s 是字符数组指针。如果传入 n,则只取数组前 n 个字符作为匹配目标;不传入 n 时,要求是以 '\0' 结尾的 C 字符串。 n 是参与匹配的字符序列长度。 c 是需要查找的单个字符。
返回值:找到时返回目标子串或者字符首字符的下标,类型为 size_type(无符号整数);查找失败返回 string::npos。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s = "hello world hello";
size_t pos;
// 查找子串hello最后一次出现的位置
pos = s.rfind("hello");
if(pos != string::npos)
cout << "hello最后一次出现下标:" << pos << endl;
// 在下标10及之前向前查找hello
pos = s.rfind("hello",10);
if(pos != string::npos)
cout << "在10之前查找hello下标:" << pos << endl;
// 查找字符'l'最后一次出现位置
pos = s.rfind('l');
if(pos != string::npos)
cout << "字符l最后一次下标:" << pos << endl;
return 0;
}
substr()函数
如图:

std::string::substr 用于截取子串,函数原型为 basic_string substr (size_type pos = 0, size_type len = npos) const。它会创建并返回一个新的 string 对象,内容是原字符串的子串。 子串从 pos 位置开始,截取 len 个字符;如果剩余字符不足 len,就截取到字符串末尾。
参数说明: pos 是子串起始下标,下标从 0 开始。pos 等于字符串长度时返回空串;pos 大于字符串长度会抛出 out_of_range 异常。 len 是要截取的字符数量。当 len 等于 string::npos 时,代表从 pos 位置一直截取到字符串末尾。 size_type 是无符号整数类型。
返回值:返回截取得到的新 string 对象,原字符串本身不会被修改。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s = "hello world";
string sub1, sub2, sub3;
// 从下标6开始,截取5个字符
sub1 = s.substr(6,5);
cout << sub1 << endl;
// 从下标0开始,npos代表截取到末尾
sub2 = s.substr(0, string::npos);
cout << sub2 << endl;
// 只传pos,len默认npos,从下标6截取到末尾
sub3 = s.substr(6);
cout << sub3 << endl;
return 0;
}
string类的非成员函数
如图:

| 函数名称 | 功能说明 |
|---|---|
| operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
| operator>> | 输入运算符重载 |
| operator<< | 输出运算符重载 |
| getline | 获取一行字符串 |
| relational operators | 大小比较 |
operator+()函数
如图:

std::operator + 用于字符串拼接,是全局函数模板,返回一个全新构造的 basic_string 对象,内容是 lhs 的字符后面拼接 rhs 的字符,不会修改原来的 lhs 和 rhs。 重载版本介绍:
- 两个 string 对象相加,string + string。
- string 对象和 C 风格字符串相加,支持 string + C 字符串、C 字符串 + string 两种写法。
- string 对象和单个字符相加,支持 string + 字符、字符 + string 两种写法。 C++11 之后新增右值引用版本,会使用移动构造提升效率,被移动的对象会处于合法但未指定的状态;两个都是右值时只会移动其中一个,另一个保持原值。
参数说明: lhs 是加号左边的操作数,rhs 是加号右边的操作数。 如果参数类型是 charT*,必须指向以 '\0' 结尾的字符序列。 charT 是 basic_string 的字符类型。
返回值:返回拼接完成后的新 string 对象,原有字符串不会被改变。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s1 = "hello";
string s2 = " world";
string res1, res2, res3;
// string + string
res1 = s1 + s2;
cout << res1 << endl;
// string + C字符串
res2 = s1 + "!!";
cout << res2 << endl;
// 字符 + string
res3 = 'A' + s1;
cout << res3 << endl;
return 0;
}
operator>>()函数
如图:

std::operator>> 是 string 的流输入运算符,用于从输入流读取字符串并存入 str,会直接覆盖 str 原来的内容。 该运算符默认以空白字符(空格、换行、制表符)作为分隔符,只能读取一个单词,遇到空白就停止读取。如果需要读取包含空格的整行文本,要使用 getline。 读取时每取出一个字符,等价于调用 push_back 追加到字符串末尾。
参数说明: is:输入流对象,用来提取字符。 str:保存读取内容的 string 对象,原有内容会被覆盖。
返回值:返回输入流 is 本身,支持连续链式输入。 读取过程中会设置流的状态标志位:
- eofbit:读取时到达输入末尾。
- failbit:读取失败;部分 eof 场景也会同时置位 failbit。
- badbit:发生严重的流错误。 如果提前设置了流异常掩码,这些状态会抛出 ios_base::failure 异常。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s;
// 输入:hello world
cin >> s;
cout << s << endl;
string s2, s3;
cin >> s2 >> s3;
cout << s2 << " | " << s3 << endl;
return 0;
}
operator<<()函数
如图:

std::operator<< 是 string 的流输出运算符,**用于把 string 中的字符序列写入输出流 os。**该运算符重载适配 basic_string,行为和 C 风格字符串的 operator<< 输出逻辑一致。
参数说明: os:输出流对象,字符会写入这个流。 str:待输出的 const 引用 string 对象,输出过程不会修改原字符串。
返回值:返回输出流 os 本身,支持链式连续输出。 输出发生错误时,流的 badbit 标志位会被置位;如果预先开启了流异常,会抛出 ios_base::failure 异常。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s1 = "Hello";
string s2 = "World";
// 链式输出
cout << s1 << " " << s2 << endl;
return 0;
}
getline()函数
如图:

std::getline 用于从输入流读取一整行字符并存入 string,会覆盖 str 中原有的内容。读取过程持续提取字符,直到遇到分隔符 delim,版本 1 支持自定义分隔符,版本 2 默认以换行符 \n 作为分隔,也会在到达文件末尾或者发生流错误时停止。如果读到分隔符,会把分隔符从流中取出并丢弃,不会存入字符串。读取字符等价于不断调用 push_back 追加到 str 末尾。
重载版本介绍: getline (is, str, delim),自定义分隔符 delim,读到 delim 停止。 getline (is, str),默认分隔符为换行符 \n,读取整行。
参数说明: is 是输入流对象,用来提取字符。 str 是用来存放读取结果的 string 对象,调用前的旧内容会被直接替换。
返回值:返回输入流 is 本身,支持链式判断。 读取时会设置流状态标志位: eofbit 代表读取时到达输入末尾。 failbit 代表读取失败,部分 eof 场景会同时设置 failbit。 badbit 代表发生严重流错误。 提前配置流异常掩码时,会抛出 ios_base::failure 异常。
重点区分:cin >> s 遇到空格就停止,getline 可以读取带空格的整行文本。注意混合使用 cin >> 和 getline 时,cin 遗留的换行符会被 getline 直接读取为空行。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s;
//读取整行,以换行符为分隔
getline(cin, s);
cout << s << endl;
string s2;
//以逗号作为分隔符
getline(cin, s2, ',');
cout << s2 << endl;
return 0;
}
relational operators
如图:
std::relational operators 是 basic_string 的关系运算符重载,包含 ==、!=、<、<=、>、>=,用来对两个字符串进行比较。比较底层调用 string 的 compare 成员函数,依据字符的 traits 规则(ASCII 码值)逐字符对比。支持 string 与 string、string 与 C 风格字符串互相比较。
比较规则:从第一个字符开始依次对比,字符 ASCII 码小的整体字符串更小;如果前面字符全部相同,短字符串更小。
返回值:返回 bool 类型,满足条件返回 true,不满足返回 false。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s1 = "apple";
string s2 = "banana";
string s3 = "apple";
cout << (s1 == s3) << endl;
cout << (s1 != s2) << endl;
cout << (s1 < s2) << endl;
cout << (s2 > s1) << endl;
cout << (s1 <= s3) << endl;
cout << (s2 >= s1) << endl;
// string 和C字符串比较
cout << (s1 == "apple") << endl;
return 0;
}

