应使用缓冲构建器而非std::string拼接,因其通过预分配和延迟拷贝避免多次内存重分配与memcpy开销,适合日志、SQL等已知长度的高吞吐场景,核心接口仅含构造、append(支持const char*和string_view)、str()三函数。

为什么不用 std::string 拼接,而要自己写缓冲构建器
频繁调用 operator+= 或 append() 会导致多次内存重分配——每次扩容都可能触发 memcpy,尤其在循环中拼接几十次小字符串时,性能损耗明显。缓冲构建器的核心价值不是“功能替代”,而是**控制预分配 + 延迟真实拷贝**。它适合日志组装、SQL 拼接、HTTP 头生成等已知大致长度、追求吞吐的场景。
关键约束:不追求线程安全、不支持随机插入/删除、不兼容 C++20 的 std::format 流式语法——这些会增加复杂度,偏离“简单”目标。
StringBuilder 的最小可行接口设计
只暴露三个函数足够:构造时指定初始容量(避免首分配)、append() 支持 const char* 和 std::string_view(避免隐式构造 std::string)、str() 返回 std::string(触发一次最终拷贝)。不提供 clear() 或 reserve()——前者可用移动赋值替代,后者由用户在构造时决策。
-
append(const char* s):跳过空指针检查(传入空指针是调用方 bug,不替用户兜底) -
append(std::string_view sv):比const std::string&更轻量,且能接受字面量和std::string子串 -
str()返回值必须是std::string,不能返回std::string_view(内部缓冲生命周期结束即失效)
缓冲区管理的关键细节:如何避免两次拷贝
常见错误是每次 append 都调用 std::vector::insert 或 std::string::append,这仍会触发边界检查和可能的扩容。正确做法是:用 std::vector<char></char> 管理原始缓冲,维护一个 size_t m_size 记录当前已用长度,append 时直接 memcpy 到 m_buffer.data() + m_size,然后更新 m_size。最后 str() 只需调用 std::string(m_buffer.data(), m_size) —— 构造函数内部会做一次拷贝,但仅此一次。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
扩容策略用 1.5 倍(非 2 倍):减少总分配次数,同时避免过度浪费。检查 m_size + len > m_buffer.capacity() 再扩容,而非 m_size + len > m_buffer.size()(否则无法利用预留空间)。
一个可直接粘贴的实现(C++17 起可用)
#include <vector>
#include <string>
#include <string_view>
#include <algorithm>
<p>class StringBuilder {
std::vector<char> m_buffer;
size_t m_size = 0;</p><pre class='brush:php;toolbar:false;'>void grow(size_t min_cap) {
size_t new_cap = std::max(m_buffer.capacity() * 3 / 2, min_cap);
m_buffer.reserve(new_cap);
}public: explicit StringBuilder(size_t capacity = 64) : m_buffer(capacity) {}
StringBuilder& append(const char* s) {
if (!s) return *this;
size_t len = std::strlen(s);
if (m_size + len > m_buffer.capacity()) {
grow(m_size + len);
}
std::memcpy(m_buffer.data() + m_size, s, len);
m_size += len;
return *this;
}
StringBuilder& append(std::string_view sv) {
size_t len = sv.length();
if (m_size + len > m_buffer.capacity()) {
grow(m_size + len);
}
std::memcpy(m_buffer.data() + m_size, sv.data(), len);
m_size += len;
return *this;
}
std::string str() const { return std::string(m_buffer.data(), m_size); }};
注意 std::memcpy 替代 std::copy:无迭代器开销,且 char 是 trivially copyable;grow() 中用整数乘除避免浮点运算;构造函数默认 64 字节——够大多数单行日志,又不至于在栈上浪费太多。
真正容易被忽略的是:这个类的移动语义没显式定义,但 std::vector 已有高效移动,所以 StringBuilder 自动获得;如果后续加了自定义析构逻辑(比如写入文件),就必须补上移动构造/赋值——但“简单”版本不需要。

















