翻译自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);