C++ std:: string append vs 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 ответов


в C++03 (для которого большая часть "cplusplus.com документация написана), сложности не были указаны, потому что разработчикам библиотеки было разрешено делать внутренние представления для строк в стиле копирования или "веревки". Например, реализация COW может потребовать копирования всей строки, если символ изменен и происходит совместное использование.

в C++11 запрещены реализации COW и rope. Следует ожидать постоянного амортизированного времени на добавленный символ или линейное амортизированное время в количестве символов, добавленных для добавления к строке в конце. Разработчики все еще могут делать относительно сумасшедшие вещи со строками (по сравнению, скажем std::vector), но большинство реализаций будут ограничены такими вещами, как "оптимизация небольших строк".

сравнение push_back и append, push_back лишает базовую реализацию потенциально полезной информации о длине, которую она может использовать для предварительного распределения пространства. С другой стороны,--2--> требуется, чтобы реализация дважды прошлась по входным данным, чтобы найти эту длину, поэтому прирост или потеря производительности будет зависеть от ряда неизвестных факторов, таких как длина строки перед попыткой добавления. Тем не менее, разница, вероятно, чрезвычайно чрезвычайно мала. Иди с append этого -- это гораздо более читабельным.


добавить еще одно мнение здесь.

я лично считаю, что лучше использовать 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), который не читается для меня.


у меня были те же сомнения, поэтому я сделал небольшой тест, чтобы проверить это (G++ 4.8.5 с профилем C++11 на Linux, Intel, 64 бит под VmWare Fusion).

и результат интересен:

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

возможно это из-за длины строки (большой), но оператор + очень дорого по сравнению с 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;
}

Да, я бы также ожидал append() чтобы выполнить лучше по причинам, которые вы дали, и в ситуации, когда вам нужно добавить строку, используя append() (или operator+=), безусловно, предпочтительнее (не в последнюю очередь также потому, что код гораздо более читаемый).

но стандарт определяет сложность операции. И это вообще линейно даже для append(), потому что в конечном счете каждый символ строки добавляется (и возможно все символы, если перераспределение происходит) необходимо скопировать (это верно, даже если memcpy или аналогичные используются).