Tip of the Week #3: String Concatenation and operator+ vs. StrCat()

Tip of the Week #3: String Concatenation and operator+ vs. StrCat()

Originally published as totw/3 on 2012-05-11
Updated 2017-09-18; revised 2018-01-22

当一个reviewer说”不要使用string的连接操作,这不高效”,通常会让提交代码的人很惊讶。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::operator+操作才能完成三个字符串的相加。在两次调用之间会构造出一个临时的字符串因此std::string foobar = foo + bar + baz等同如下:

std::string temp = foo + bar;
std::string foobar = std::move(temp) + baz;

具体来说就是foo和bar的内容在放入foobar之前必须先复制到一个临时位置(有关std::move,看Tip of the Week #77: Temporaries, moves, and copies)。

C++11允许第二次连接操作的时候不需要创建一个新的string对象的:std::move(temp) + baz 等同于std::move(temp.append(baz))。然后有可能其内部buffer大小不够导致内存重新分配(会导致额外的拷贝),因此在最坏的情况下,n字符串连接的时候需要O(n)次内存重分配。

一个好的替代方法就是使用absl::StrCat(),一个不错的帮助函数其实现在 absl/strings/str_cat.h文件中,通过计算必要的字符串长度,预先分配大小,并将所有输入数据进行连接,其复杂度优化到O(n),同样对于以下情况:

foobar += foo + bar + baz;

使用abs::StrAppend可以带来同样的优化:

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

同样,absl::StrCat() and absl::StrAppend()对除了字符串类型意外的类型进行操作: 可以使用absl::StrCat/absl::StrAppendint32_t, uint32_t, int64_t, uint64_t, float, double, const char*, and string_view等类型进行转换,像如下这样:

std::string foo = absl::StrCat("The year is ", year);
©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值