std::string::capacity() 返回当前分配但未使用的字符空间大小(以字节计),单位是 size_type;它反映底层缓冲区能容纳多少字符而不触发重新分配,空字符串的 capacity() 可能非零,clear() 不降低 capacity(),reserve() 仅在请求容量大于当前 capacity() 时重分配。

std::string::capacity() 返回什么值
capacity() 返回当前分配但未使用的字符空间大小(以字节计),单位是 size_type,不是字符串实际长度。它反映底层缓冲区能容纳多少字符而不触发重新分配。
常见误解是认为 capacity() 等于 size() 或受构造方式“固定”。实际上:
- 空字符串的
capacity()可能非零(如 GCC 的 SSO 实现常为 15 或 22) - 通过
std::string s = "hello"构造后,capacity()可能大于size(),也可能相等,取决于编译器和优化级别 - 调用
clear()不会降低capacity();内存仍保留
std::string::reserve() 的作用与触发条件
reserve() 显式要求底层分配至少指定数量的字符空间。它只增不减,且仅在请求容量 > 当前 capacity() 时才真正重分配。
典型使用场景:已知后续要拼接大量数据(如读取文件、解析日志行),提前预留可避免多次 realloc + memcpy。
立即学习“C++免费学习笔记(深入)”;
注意点:
-
reserve(n)中n是字符数,不是字节数(UTF-8 多字节字符仍按一个char计) - 若
n ,调用无任何开销,也不改变现有内容 - 调用后
size()不变,字符串内容不变,只是缓冲区变大了 - 不能用
reserve()来“截断”字符串——那是resize()或shrink_to_fit()的事
reserve() 和 shrink_to_fit() 的关键区别
reserve() 是向上调整容量上限,shrink_to_fit() 是向下尝试释放多余内存(C++11 起)。但后者是“提示”,实现可忽略。
示例对比:
std::string s = "1234567890"; s.reserve(100); // capacity ≥ 100(通常就是 100) s += "xxxxxxxxxx"; // 追加 10 字符,size=20,capacity 仍 ≥ 100 s.shrink_to_fit(); // capacity 可能回落到 20 或略高,但不保证
常见误用:
- 对小字符串反复
reserve()→ 频繁分配反而更慢 - 以为
shrink_to_fit()一定生效 → 在 libstdc++ 中可能无效,在 MSVC 中较可靠 - 在循环内对同一 string 每次都
reserve(s.size() + N)→ 容量阶梯增长,不如一次性预估总长
SSO 对 capacity() 和 reserve() 的实际影响
几乎所有主流 STL 实现(libstdc++、libc++、MSVC)都对小字符串启用 SSO(Short String Optimization)。这意味着:
- 当字符串长度 ≤ 阈值(常见为 15/22/23 字节)时,字符直接存于对象内部,不堆分配
- 此时
capacity()返回的是 SSO 缓冲区总长(含终止符位),不是堆上容量 -
reserve()在 SSO 区域内调用无效(因为没堆内存可扩);一旦超过 SSO 阈值,首次扩容才真正 malloc
验证方式:
std::string s; std::cout << "empty capacity: " << s.capacity() << "\n"; // 常见输出 15 或 22 s = std::string(20, 'x'); // 强制堆分配 std::cout << "20-char capacity: " << s.capacity() << "\n"; // 通常 ≥ 20,可能是 32/48/64
所以别依赖 capacity() 判断是否“已堆分配”——它只告诉你当前可用空间,不管来源。
真正需要预分配时,先估算最大长度,再一次性 reserve();频繁小量追加又不确定总量,不如交给默认策略。SSO 让小字符串几乎零成本,而盲目 reserve 反而可能绕过它、强制堆分配。











