必须预分配内存,否则append()或+=会退化为O(n²);因每次扩容需realloc、memcpy旧内容、析构旧缓冲,总拷贝量接近平方级;正确做法是循环前一次性reserve(total_len),并留5–10%余量。

大规模重复连接必须预分配内存,否则 append() 或 += 会退化成 O(n²) 行为——这不是“慢一点”,而是数据量翻倍时耗时可能翻 4 倍以上。
为什么没 reserve 就会 O(n²)?
每次 append() 发现容量不足,std::string 就得 realloc:申请新内存、memcpy 旧内容、析构旧缓冲。1000 次拼接若初始容量仅 15(SSO 上限),可能触发 10+ 次扩容,每次拷贝量递增,总 memcpy 字节数接近平方级。
- 典型症状:
perf显示大量malloc/memcpy占比飙升,或 valgrind 报告高频堆分配 - SSO 不是万能的:一旦超出栈内缓冲(如 15–22 字节),立刻退化到堆分配
-
operator+更糟:每轮都构造临时std::string,开销翻倍
reserve() 怎么调才真正生效?
关键不是“调了”,而是“调得准”:只 reserve 一次,且在循环前完成;传入值必须 ≥ 实际总长,否则仍会中途扩容。
- 错误写法:
s.reserve(s.size() + add_len)放在循环里——这等于没 reserve,容量仍按指数增长 - 正确做法:先遍历求和,再
s.reserve(total_len)。例如拼接 1000 个"abc":s.reserve(1000 * 3) - 留 5–10% 余量更稳妥:避免因内存对齐或 padding 触发最后一次扩容,但别盲目
reserve(1MB)只拼 100 字节 -
reserve(0)合法但无意义;resize()不能替代reserve()——它会填充'\0'并改变size(),后续拼接从错误位置开始
append() 参数选错,性能直接打折
看似只是写法差异,但传错重载版本会让单次追加多出一次临时构造或 strlen() 扫描,百万次就是可观延迟。
立即学习“C++免费学习笔记(深入)”;
- 避免
s.append("hello"):语义要求 null-terminated 扫描,编译器不一定优化掉 - 改用
s.append("hello", 5)或s.append(std::string_view{"hello"}),长度明确、零扫描 - 子串来自
std::string&?直接s.append(other),现代 STL 会自动 move - 子串来自
std::string_view sv?用s.append(sv)或s.append(sv.data(), sv.size()),零拷贝 - 绝对不要
s.append(some_c_str):又触发一次strlen(),尤其当some_c_str很长时
极端情况:总长完全未知怎么办?
无法一次性算出总长时,分两轮最稳:第一轮只累加长度(不拼),第二轮才真正拼接。
- 第一轮:用
size_t total = 0遍历所有源,累加sv.size()、s.length()、分隔符长度等,过程中做溢出检查(如if (total > SIZE_MAX - next.size())) - 第二轮:
s.reserve(total)后再循环append() - 如果连第一轮遍历都成本过高(如流式读取),可设保守上限并配合
shrink_to_fit()后续回收冗余内存 - 注意:
std::string::data()在 C++17 起返回可写指针,但写入前必须已reserve()且通过resize()或operator[]控制边界,否则未定义行为
最容易被忽略的是:reserve() 后 size() 仍是 0,capacity() 才变大;你得用 append() 或 += 写入内容,不能指望 reserve() 自动填满字符串。


















