c++oding="utf-8" ?>
预估总长度后调用 reserve() 再 append() 或 +=,可避免多次内存重分配;否则反复拼接最坏 o(n²),因每次扩容需复制旧数据;reserve 过度或不足均降低性能。

直接用 std::string::reserve() 配合 += 或 append(),比反复拼接快数倍——前提是能预估总长度;否则 reserve 过度或不足都会拖慢性能。
为什么反复 += 拼接字符串很慢
每次 += 可能触发内存重分配:原 buffer 不够时,std::string 会申请新空间(通常是 1.5× 或 2× 当前容量),复制旧内容,再销毁旧内存。N 次拼接最坏 O(N²) 时间 + 多次 memcpy。
- 典型场景:
for (auto& s : vec) result += s;,vec有 1000 个平均长 20 字节的字符串 → 至少几十次 realloc - 即使编译器做了 SSO(短字符串优化),一旦超出栈内缓冲(通常 15–22 字节),就立刻退化到堆分配
-
operator+更糟:临时对象多、拷贝更频繁,应完全避免在循环中使用
怎么用 reserve() 才真正高效
关键不是“调了 reserve() 就快”,而是“预留得准”:过大会浪费内存,过小仍要 realloc。必须先算出总长度。
- 如果所有子串长度已知(如 vector of string):
size_t total = std::accumulate(vec.begin(), vec.end(), size_t{0}, [](size_t sum, const std::string& s) { return sum + s.size(); }); result.reserve(total); - 如果含格式化(如
"%d:%s"),需手动估算:数字位数用std::to_string(x).size()或 log10+1,字符串用.size(),常量部分直接加 - 避免
reserve(0)或reserve(1):这不会禁用增长逻辑,反而可能因初始 capacity=0 导致首次+=就分配 - reserve 后不要调用
clear()再拼接——clear()不释放内存,但若后续拼接远小于 reserve 值,capacity 会一直虚高,影响后续 move 或 swap
append() 比 += 稍快?看场景
两者语义一致,但 append() 是显式接口,部分标准库实现对它做了微优化(比如避免隐式构造临时 std::string)。不过差异通常在纳秒级,真正起决定作用的是是否 reserve。
- 推荐写法:
result.append(s.data(), s.size());(避免构造std::string临时对象,尤其当s是const char*或std::string_view) - 对
std::string_view直接append(sv)安全且零开销;而+= sv在某些老 libstdc++ 版本中可能隐式转成std::string - 不要为了用
append()而拆分逻辑——可读性下降得不偿失,优先保证 reserve 正确性
容易被忽略的边界:移动语义和 small string
reserve 的收益在大字符串拼接(>1KB)上才明显;对几个短字符串,SSO 可能让未 reserve 的版本更快(无 malloc 开销)。
- 移动后
std::string的 capacity 不保留:若你std::move(result)给别人,接收方拿到的是一个 capacity ≈ size 的字符串,之前 reserve 的空间白费 - 跨线程传递时注意:reserve 是线程安全的,但拼接过程(
+=/append())必须加锁,否则 data race - 调试时用
result.capacity()和result.size()打印验证是否真没 realloc——别只信“我写了 reserve”
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











