std::string += 在循环中拼接很慢,因为每次可能触发内存重分配,导致最坏 O(n²) 时间复杂度;应先 reserve() 预分配容量再 append(),避免重复 realloc。

为什么 std::string += 在循环中拼接很慢?
因为每次 += 都可能触发内存重分配 —— 底层 std::string 的缓冲区不够时,会申请新空间、拷贝旧内容、释放旧内存。重复 1000 次,最坏情况发生 1000 次 realloc,时间复杂度接近 O(n²)。
典型表现:字符串越长、拼接轮次越多,性能断崖式下降;用 valgrind --tool=callgrind 或 perf 能看到大量 malloc/memcpy 占比飙升。
- 避免在循环内直接
s += part,尤其当part长度不确定或总拼接量较大时 - 若已知最终长度(比如拼接固定次数的相同子串),优先调用
s.reserve(n) -
reserve()不改变size(),只预分配容量;调用后capacity() >= n才算生效
用 std::string::reserve() 预分配 + append() 是最稳方案
这是标准库层面最直接、零依赖、跨编译器稳定的优化方式。关键是把“预估总长度”做准 —— 算错只会多占点内存,不算错就可能退化回多次 realloc。
例如拼接 1000 次 "abc":
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
std::string s;
s.reserve(1000 * 3); // 关键:提前 reserve
for (int i = 0; i < 1000; ++i) {
s.append("abc"); // append 比 += 更明确语义,且部分实现对 append 做了小优化
}- 用
append()替代+=无本质性能差异,但语义更清晰,且某些 STL 实现对append(const char*, size_t)有特化路径 - 如果子串来自
std::string_view或 C 字符串,优先用append(sv)或append(ptr, len),避免隐式构造临时std::string - 不要写
s.reserve(s.size() + add_len)在循环里 —— 这等于没 reserve,每次仍可能 realloc
大量小字符串拼接?考虑 std::ostringstream 或 fmt::format
当拼接对象类型混杂(数字、字符串、变量)、数量不定、且难以预估总长时,std::ostringstream 更安全:它内部也做缓冲管理,且自动处理类型转换。
但注意:std::ostringstream 构造/析构有开销,纯字符串拼接不如 reserve+append 快;C++20 后推荐用 std::format(需 -std=c++20),但目前 GCC/Clang 支持还不全。
- 简单场景别为了“看起来高级”而用
ostringstream—— 多一个流对象、一次str()拷贝,不划算 - 若项目已用
fmt库,fmt::format("{}{}{}", a, b, c)比手写循环更简洁,且fmt::format_to支持写入预分配 buffer - 绝对避免在 hot path 中反复构造
std::ostringstream对象;可复用,但要注意clear()不清缓冲区,需str("")
极端性能场景:手动管理 buffer(如用 std::vector<char></char>)
当拼接逻辑极热(每秒数百万次)、且字符串内容完全可控(比如日志行组装、协议编码),可以绕过 std::string 的封装,直接操作 raw buffer。
核心思路:用 std::vector<char></char> 当动态数组,resize() 或 reserve() 配合 memcpy 或 std::copy 批量写入。
- 必须自己维护 null terminator(如果需要 C 兼容);
std::string自动处理,这里得手动加v.push_back('\0') - 拼接完成后转成
std::string时,用std::string(v.data(), v.size()),避免额外拷贝(C++11 起支持 data() 返回 const char*) - 这种写法破坏可读性,调试困难,仅在 profiler 明确指出 string 拼接是瓶颈、且其他优化无效时才考虑
预分配和批量写入的意识比具体用哪个 API 更重要 —— 很多人卡在“怎么拼”,其实关键在“别让内存反复折腾”。

















