std::string::reserve()必须在循环外一次性调用,遍历所有源累加长度后调用,预留5–10%余量;append应优先用string_view或指定长度的重载;reserve不改变size(),不可直接operator[]写入;重复字符用std::string(n, c)构造。

std::string::reserve()必须在循环外一次性调用
不这么做,性能不会提升,反而可能更差。常见错误是把s.reserve(s.size() + add_len)写在循环里——这等价于没调用,容量仍按1.5×或2×指数增长,realloc次数一点没减。
真正有效的做法只有一条:拼接前遍历所有源,累加长度,再调一次reserve()。例如拼接1000个"abc",直接s.reserve(3000);若源是std::vector<:string_view></:string_view>,用std::accumulate求和:
size_t total = std::accumulate(vec.begin(), vec.end(), size_t{0},
[](size_t sum, const auto& sv) { return sum + sv.size(); });留5–10%余量更稳妥(比如reserve(total * 1.1)),避免因内存对齐或padding触发最后一次扩容;但别盲目reserve(1MB)只拼100字节,浪费内存。
append()参数选错会白费reserve()
reserve()只是把内存准备好,能不能真正避免拷贝,取决于你后续怎么append()。传std::string对象或const char*会隐式构造临时对象或重复扫描null终止符,开销叠加百万次就是可观延迟。
立即学习“C++免费学习笔记(深入)”;
- ✅ 推荐:
s.append(std::string_view{"hello"})或s.append("hello", 5)—— 长度明确、零构造、零strlen()扫描 - ✅ 源是
char*且长度已知:s.append(ptr, len),比s.append(ptr)少一次strlen() - ❌ 避免:
s.append(std::string("world"))或s += "foo"—— 每次都构造+析构临时std::string - ⚠️ 注意:
append(s, pos, len)要求pos + len <= s.size(),否则未定义行为;而s.substr(...)会先分配堆内存拷贝,纯属冗余
reserve()后size()仍是0,不能直接operator[]写入
reserve()只改变capacity(),不改变size(),也不初始化内存。误写s.reserve(100); s[0] = 'x';是未定义行为——哪怕编译通过,运行时可能崩溃或静默出错。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
安全写法只有两种:
- 继续用
append()或+=:它们内部自动更新size(),并在预留范围内直接memcpy - 想跳过边界检查、追求极致可控性:先
s.resize(total_len)(不是reserve),再用std::copy(sv.begin(), sv.end(), &s[i])或s.data()[i] = ch
特别注意:resize()会填'\0'并改变size(),后续append()会从新size()位置开始写,中间填充字节可能干扰逻辑(比如生成的JSON含多余空字符)。
单字符重复别用循环append
需要重复拼接同一个字符(如生成1000个'-'),绝对不要写for (int i = 0; i < 1000; ++i) s.append(1, '-');——它慢10倍以上,还容易误写成s.append('-')导致编译失败(append(char)不存在)。
正确做法只有一种:std::string(1000, '-')。底层调用memset,零迭代、零检查、无临时对象。
混合场景(如"prefix" + std::string(1000, '-') + "suffix")也适用:先构造单字符串,再统一append(),比逐段拼更可控、更少分支。

















