C++ std::string 使用超全攻略(2026 年最新实用版)
std::string 是现代 C++ 中处理字符串的首选工具(远优于 char* / char[]),它自动管理内存、支持动态增长、提供丰富接口。
核心原则(先背这几条):
- 包含头文件:
#include <string> - 常用命名空间:
using namespace std;或using std::string; - 底层实现:连续存储 + SSO(小字符串优化) → 小字符串(通常 ≤15/23 字节,视编译器)不堆分配
- C++11 后:支持移动语义、初始化列表、范围 for
- C++17:
string_view大放异彩 - C++20:
contains()、starts_with()、ends_with() - C++23:
resize_and_overwrite、contains()更稳固、string::npos等细微改进
1. 构造方式(最常用 6 种)
string s1; // 空串
string s2 = "hello"; // 从 const char*
string s3("world", 3); // 前 3 个字符 → "wor"
string s4(5, 'x'); // "xxxxx"
string s5(s2); // 拷贝构造
string s6{'a','b','c'}; // 初始化列表 (C++11+)
string s7 = s2 + " " + s3; // 拼接构造
string s8(s2.begin(), s2.begin()+3); // 迭代器范围
2. 元素访问(4 种方式对比)
| 方式 | 语法 | 越界行为 | 返回类型 | 推荐场景 |
|---|---|---|---|---|
| operator[] | s[i] | 未定义行为 | char& / char | 已知安全,追求速度 |
| at() | s.at(i) | 抛 out_of_range | char& / char | 需要边界检查 |
| front() | s.front() | 未定义(空串) | char& | 第一个字符 |
| back() | s.back() | 未定义(空串) | char& | 最后一个字符 |
| data() / c_str() | s.data() / s.c_str() | — | const char* | 传给需要 C 风格字符串的函数 |
C++17+ 推荐:data() 可写(非 const),c_str() 永远 const。
3. 容量与大小相关(高频)
size_t len = s.size(); // 或 s.length() (完全等价)
bool empty = s.empty(); // 比 size()==0 更快
size_t cap = s.capacity(); // 当前分配空间(≥ size)
s.reserve(100); // 预分配,至少 100,防多次重分配
s.resize(10); // 调整到 10,长了截断,短了补 '\0'
s.resize(15, 'z'); // 短了补 'z'
s.shrink_to_fit(); // 请求释放多余容量(不保证成功)
C++23 新增(很实用):
s.resize_and_overwrite(新长度, [](char* data, size_t size) {
// 在 data[0..size) 范围内原地修改,返回实际想用的最终长度
return 新实际长度;
});
4. 修改操作(最常用分类)
4.1 追加 / 插入
s += " world"; // operator+=
s.append("!!!"); // append
s.push_back('?'); // 单个字符
s += 'A'; // 也行
s.insert(5, " inserted"); // 在下标 5 前插入
s.insert(s.begin()+3, 2, '*'); // 迭代器位置插入 2 个 '*'
// C++11+ emplace 家族(就地构造,微性能提升)
s.emplace_back('!');
s.emplace(0, "pre"); // 前插
4.2 删除
s.pop_back(); // 删最后一个字符
s.erase(5); // 从下标 5 开始全删
s.erase(2, 4); // 从 2 开始删 4 个
s.erase(s.begin()+1, s.end()-1);// 范围删除
s.clear(); // 清空(size=0,capacity 不一定释放)
4.3 替换 / 赋值
s = "new content"; // 赋值
s.assign("reset"); // 等价于 =
s.replace(2, 3, "good"); // 从 2 开始 3 个字符替换成 "good"
5. 查找与比较(面试 + 实战高频)
| 函数 | 含义 | 返回值 | C++ 版本 |
|---|---|---|---|
| find(substr) | 第一次出现位置 | size_t / npos | — |
| rfind() | 最后一次出现 | 同上 | — |
| find_first_of(chars) | 任意一个 chars 第一次出现 | 同上 | — |
| find_last_not_of() | — | — | — |
| contains(substr) | 是否包含子串 | bool | C++20+ |
| starts_with() | 是否以某前缀开头 | bool | C++20+ |
| ends_with() | 是否以某后缀结尾 | bool | C++20+ |
| compare() | 字典序比较 | <0 / 0 / >0 | — |
npos 是 string::npos → static const size_t,通常是 -1(全 1)
if (s.find("error") != string::npos) { ... }
if (s.contains("bug")) { ... } // C++20 更清晰
6. 子串与分割(常见需求)
string sub = s.substr(3, 5); // 从 3 开始取 5 个
string sub2 = s.substr(7); // 从 7 到末尾
// C++17+ 推荐用 string_view 做无拷贝子串
string_view view = s.substr(2,10); // 零拷贝视图
手动分割示例(最常用写法):
vector<string> split(const string& s, char delim = ' ') {
vector<string> res;
size_t start = 0;
while (true) {
size_t pos = s.find(delim, start);
if (pos == string::npos) {
res.push_back(s.substr(start));
break;
}
res.push_back(s.substr(start, pos - start));
start = pos + 1;
}
return res;
}
7. 现代 C++ 推荐搭配(2026 年必知)
- string_view(C++17) → 零拷贝只读视图,函数参数首选
- format(C++20) →
std::format("Hello {}!", name) - ranges + views(C++20) → 更函数式处理字符串
- from_chars / to_chars(C++17
<charconv>) → 最高性能数字 ↔ 字符串转换
8. 常见误区 & 性能Tips
- 误区:
string s = "";后反复s += ch(多次重分配)→ 用reserve预估大小 - 误区:
while(getline(cin,s))里反复s.clear()而不是复用 - 误区:传
string参数时不写const &或string_view - Tips:小字符串 SSO → 短串拼接极快
- Tips:
+=/append多个小串时,编译器常做 RVO 优化
你现在最想看哪部分深入代码?
- 完整 split / join / trim 实现?
- string + regex 高级用法?
- string_view vs string 真实性能对比?
- C++20 format / chrono 格式化日期到 string?
- 还是来几道经典面试手撕 string 题目?
告诉我,继续陪你把 string 玩透~