翻译自Tip of the Week #3: String Concatenation and operator+ vs. StrCat()
本文主题是字符串连接string::operator+absl::StrCat()

我们经常听到有人说使用操作符(+)连接字符串不够高效,事实真的如此么?

实际上,以下两段代码拥有相近的性能:

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);

如果连接3个字符串呢?答案可能会发生变化。比如下面两段代码,性能是有差异的:

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::operator+,过程中会创建一个临时的str::string

std::string foobar = foo + bar + baz等价于下面的写法:

std::string temp = foo + bar;   // 注意这里还会有拷贝操作
std::string foobar = std::move(temp) + baz;

在C++11中,std::move(temp) + baz等价于std::move(temp.append(baz))这种写法,这可能会减少一次string对象的构造。为什么说是“可能”呢?因为,有这样一种可能:最初为temp分配的内存不足以存放最终的结果,这种情况需要额外一次reallocation(当然也有额外一次copy)。最终结果就是,最坏的情况下,连接n个字符串需要O(n)次reallocation。

absl/strings/str_cat.h中的absl::StrCat()在连接字符串时,会计算好结果的长度,并预留合适的内存。因此本文更推荐使用absl::StrCat()

同样地,

foobar += foo + bar + baz;

可以用下面的代码代替:

absl::StrAppend(&foobar, foo, bar, baz);

除了字符串,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);