Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;std::字符串附加vs push_back()_C++_String_Append_Push Back - Fatal编程技术网

C++ C++;std::字符串附加vs push_back()

C++ C++;std::字符串附加vs push_back(),c++,string,append,push-back,C++,String,Append,Push Back,这真的是一个仅出于我自身利益的问题,我无法通过文档确定 我从中看出,附加具有复杂性: 未指定,但在新字符串长度中通常为线性 而push_back()具有复杂性: 未指定;通常为摊销常数,但在新字符串长度中为线性 作为一个玩具示例,假设我想将字符“foo”附加到字符串中。会 myString.push_back('f'); myString.push_back('o'); myString.push_back('o'); 及 完全一样的东西?还是有什么区别?您可能会认为append会更有效,因为

这真的是一个仅出于我自身利益的问题,我无法通过文档确定

我从中看出,附加具有复杂性:

未指定,但在新字符串长度中通常为线性

而push_back()具有复杂性:

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

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

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


完全一样的东西?还是有什么区别?您可能会认为append会更有效,因为编译器会知道需要多少内存才能将字符串扩展到指定的字符数,而push_-back可能需要确保每次调用的内存?

是的,出于您给出的原因,我还希望
append()
执行得更好,在需要追加字符串的情况下,使用
append()
(或
operator+=
)当然更可取(尤其是因为代码可读性更高)

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

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

在C++11中,禁止使用COW和rope实现。您应该期望每个添加的字符有固定的摊销时间,或者在字符串末尾添加的字符数中有线性摊销时间。实现者可能仍然会对字符串做一些相对疯狂的事情(比如说,与std::vector相比),但大多数实现都会局限于“小字符串优化”之类的事情


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

在此处再添加一个意见

我个人认为,在从另一个字符串中逐个添加字符时,最好使用<代码> PurthBook()/代码>。例如:

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

如果在这里使用
append()
,我会用
append(1,it)
替换
push_-back(it)
,这对我来说不是那么容易理解。

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

结果很有趣:

push :19 append :21 ++++ :34 推送:19 附加:21 ++++ :34 这可能是因为字符串长度(大),但是运算符+与push_-back和append相比非常昂贵

另外有趣的是,当操作符只接收一个字符(不是字符串)时,它的行为与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;
}
时间计数器vCounter;
{
字符串vTest;
vCounter.start();

对于(int-vIdx=0;vIdxBesides-the-besides-the-besides-the-besides-the-besides-the-besides-the-besides-besides-the-besides-the-besides-besides-the-besides-the-besides-besides-the-besides-besides-besides-besides-besides-besides-besides-the-besi。虽然不幸的是,他们没有说明
追加
的典型情况,但只有最坏的情况。@JoachimPileborg:
推回
必须按固定时间摊销,因此我所知道的每个实现三次分配的可能性为零。大多数从合理的大小开始(大于1或2)并以几何方式增长基础缓冲区(按1.5倍或每个大小增加2倍)。需要复制附加字符串的每个字符。如果不需要调整基础内存块的大小,则不需要复制字符串的现有内容。但是,如果只想附加1个字符,则没有
附加(字符)
重载。在这种情况下,可以使用
push_back()
。@rustyx当然可以,但那将是另一个问题:)你知道C++11/14/17标准是否对字符串的
push_back
/
append
/
insert
的复杂性要求进行了严格的限制?如果没有,您确定大多数现有的实现都是如此友好吗?(我隐约记得过去在这些方面存在问题,但这可能只是一些CoW实现的结果。)@尼莫:我不确定你能要求什么样的“紧缩”。据我所知,它们一直是按固定时间摊销的。目前它是N4606 23.2.3[sequence.reqmts]/16@BillyONeal:我的意思是相对于C++03,在C++03中,
push_back
等。字符串基本上没有复杂度保证(不像vector)。我看到这是针对C++11中字符串上的
push_back
进行的更正,谢谢您的参考。但据我所知,该标准没有对字符串的
insert(end(),…)
append()
施加任何复杂性要求。因此从“严格的
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;
}