当代码reviower人说:“不要使用字符串连接操作符,它不是那么高效”时,用户常常感到惊讶。std::string::operator+
不高效?搞错了吧?
事实证明,这样的低效率并不是显而易见的。实际上,这两个代码段的执行时间几乎相同:
std::string foo = LongString1();
std::string bar = LongString2();
std::string foobar = foo + bar;
std::string foo = LongString1();
std::string bar = LongString2();
std::string foobar = absl::StrCat(foo, bar);
但是,这两个片段的情况并非如此:
std::string foo = LongString1();
std::string bar = LongString2();
std::string baz = LongString3();
string foobar = foo + bar + baz;
std::string foo = LongString1();
std::string bar = LongString2();
std::string baz = LongString3();
std::string foobar = absl::StrCat(foo, bar, baz);
当我们把foo+bar+baz表达式中发生的事情分开时,可以理解这两种情况不同的原因。因为C++中没有三个参数操作符的重载,所以这个操作必然会对String::在这两个调用之间,操作将构造(和存储)一个临时字符串。所以STD::字符串foobar = foo + bar + baz
确实等价于:
std::string temp = foo + bar;
std::string foobar = std::move(temp) + baz;
具体来说,请注意,在将foo和bar的内容放入foobar之前,必须将它们复制到临时位置。C++ 11至少允许第二个级联发生,而不需要创建一个新的std::move(temp) + baz。
但是,最初为临时文件分配的缓冲区可能不够大,无法容纳最后一个字符串,在这种情况下,需要重新分配(和另一个副本)。因此,在最坏的情况下,N个字符串连接链需要O(N)重新分配
最好使用absl::StrCat()
,absl/strings/str_cat.h是一个很好的助手函数,它计算必要的字符串长度,保留该大小,并将所有输入数据连接到输出中—一个优化良好的O(n)
。同样,对于以下情况:
foobar += foo + bar + baz;
使用absl::StrAppend()
,它执行类似的优化:
absl::StrAppend(&foobar, foo, bar, baz);
此外,absl::StrCat()
和absl::StrAppend()
对字符串类型以外的类型进行操作,可以使用absl::StrCat
/absl::StrAppend
转换为int32_t
, uint32_t
, int64_t
, uint64_t
, float
, double
, const char*
, 和string_view
,如下所示:
std::string foo = absl::StrCat("The year is ", year);