C ++ std :: string append vs push_back()

时间:2013-02-26 05:41:08

标签: c++ string append push-back

这真的是一个问题,仅仅是出于我自己的兴趣,我无法通过文档来确定。

我在http://www.cplusplus.com/reference/string/string/上看到附加内容具有复杂性:

“未指定,但通常在新字符串长度中达到线性。”

而push_back()具有复杂性:

“未指定;通常摊销常数,但在新字符串长度中最多为线性。”

作为一个玩具示例,假设我想将字符“foo”附加到字符串中。将

myString.push_back('f');
myString.push_back('o');
myString.push_back('o');

myString.append("foo");

完全相同的东西?或者有什么区别?您可能认为追加会更有效率,因为编译器会知道将字符串扩展指定数量的字符需要多少内存,而push_back可能需要在每次调用时保护内存?

4 个答案:

答案 0 :(得分:33)

在C ++ 03中(大多数“cplusplus.com”的文档都是编写的),复杂性未被指定,因为库实现者被允许进行Copy-On-Write或“rope-style”内部表示字符串。例如,如果修改了一个字符并且正在进行共享,那么COW实现可能需要复制整个字符串。

在C ++ 11中,禁止COW和绳索实现。您应该期望每个字符添加的常量摊销时间或线性摊销时间在最后添加到字符串中添加的字符数。实施者可能仍然使用字符串做相对疯狂的事情(与std::vector相比),但大多数实现将仅限于“小字符串优化”之类的事情。

在比较push_backappend时,push_back剥夺了可能用于预分配空间的潜在有用长度信息的基础实现。另一方面,append要求实现遍历输入两次以找到该长度,因此性能增益或损失将取决于许多不可知因素,例如字符串之前的长度你试图追加。也就是说,差异可能非常极小。为此,请使用append - 它更具可读性。

答案 1 :(得分:3)

在此处再添加一条意见。

我个人认为在从另一个字符串逐个添加字符时使用push_back()会更好。例如:

string FilterAlpha(const string& s) {
  string new_s;
  for (auto& it: s) {
    if (isalpha(it)) new_s.push_back(it);
  }
  return new_s;
}

如果在此使用append(),我会将push_back(it)替换为append(1,it),这对我来说是不可读的。

答案 2 :(得分:3)

我有同样的疑问,所以我做了一个小测试来检查这个(g ++ 4.8.5在Linux上使用C ++ 11配置文件,在VmWare Fusion下使用64位)。

结果很有趣:

push :19
append :21
++++ :34

这可能是因为字符串长度(大),但是与push_back和append相比,operator +非常昂贵。

另外有趣的是,当操作符只接收一个字符(不是字符串)时,它的行为与push_back非常相似。

为了不依赖于预先分配的变量,每个周期都在不同的范围内定义。

注意:vCounter只使用gettimeofday来比较差异。

TimeCounter vCounter;

{
    string vTest;

    vCounter.start();
    for (int vIdx=0;vIdx<1000000;vIdx++) {
        vTest.push_back('a');
        vTest.push_back('b');
        vTest.push_back('c');
    }
    vCounter.stop();
    cout << "push :" << vCounter.elapsed() << endl;
}

{
    string vTest;

    vCounter.start();
    for (int vIdx=0;vIdx<1000000;vIdx++) {
        vTest.append("abc");
    }
    vCounter.stop();
    cout << "append :" << vCounter.elapsed() << endl;
}

{
    string vTest;

    vCounter.start();
    for (int vIdx=0;vIdx<1000000;vIdx++) {
        vTest += 'a';
        vTest += 'b';
        vTest += 'c';
    }
    vCounter.stop();
    cout << "++++ :" << vCounter.elapsed() << endl;
}

答案 3 :(得分:0)

是的,我还希望append()因您提供的原因而表现更好,并且在您需要附加字符串的情况下,使用append()(或operator+=)当然更可取(尤其是因为代码更具可读性)。

但标准规定的是操作的复杂性。即使对于append(),这通常是线性的,因为最终要追加的字符串的每个字符(以及可能的所有字符,如果发生重新分配)都需要被复制(即使memcpy或类似的情况也是如此使用)。