Подтвердить что ты не робот

С++ std:: ostringstream vs std::string:: append

Во всех примерах, которые используют некоторую буферизацию, я вижу, что они используют поток вместо строки. Как std:: ostringstream и < оператора, отличного от использования string.append. Какой из них быстрее, а какой меньше ресурсов (памяти).

Единственное различие, которое я знаю, заключается в том, что вы можете выводить различные типы в выходной поток (например, целочисленный), а не ограниченные типы, которые принимает строка:: append.

Вот пример:

std::ostringstream os;
os << "Content-Type: " << contentType << ";charset=" << charset << "\r\n";
std::string header = os.str();

против

std::string header("Content-Type: ");
header.append(contentType);
header.append(";charset=");
header.append(charset);
header.append("\r\n");

Очевидно, использование потока короче, но я думаю, что append возвращает ссылку на строку, поэтому ее можно записать следующим образом:

std::string header("Content-Type: ");
header.append(contentType)
  .append(";charset=")
  .append(charset)
  .append("\r\n");

И с выходным потоком вы можете сделать:

std::string content;
...
os << "Content-Length: " << content.length() << "\r\n";

Но как насчет использования памяти и скорости? Особенно при использовании в большой петле.

Update:

Чтобы быть более понятным, вопрос: Какую пользу я должен использовать и почему? Есть ли ситуации, когда кто-то предпочитает или другой? Для производительности и памяти... ну, я думаю, что эталонный тест - единственный способ, поскольку каждая реализация может отличаться.

Обновление 2:

Ну, я не понимаю, что я должен использовать из ответов, что означает, что любой из них выполнит эту работу, плюс вектор. Cubbi сделал хороший бенчмарк с добавлением Dietmar Kühl, что самая большая разница в конструкции этих объектов. Если вы ищете ответ, вы должны это проверить. Я буду ждать немного больше для других ответов (смотрите предыдущее обновление), и если я не получу один, я думаю, что я соглашусь с ответом Толга, потому что его предложение использовать вектор уже сделано, прежде чем это означает, что вектор должен быть менее ресурсоемким.

4b9b3361

Ответ 1

std::ostringstream не обязательно сохраняется в виде последовательного массива символов в памяти. Вам действительно нужно иметь непрерывный массив символов при отправке этих HTTP-заголовков, и это может скопировать/изменить внутренний буфер, чтобы сделать его последовательным.

std::string с использованием соответствующего std::string::reserve не имеет причин действовать медленнее, чем std::ostringstream в этой ситуации.

Однако std::ostringstream, вероятно, быстрее для добавления, если вы абсолютно не знаете, какой размер вы должны зарезервировать. Если вы используете std::string, и ваша строка растет, в конечном итоге требуется перераспределение и копирование всего буфера. Было бы лучше использовать один std::ostringstream::str(), чтобы сделать последовательные данные сразу же по сравнению с несколькими перераспределениями, которые произойдут иначе.

P.S. Pre-С++ 11 std::string не обязательно должен быть последовательным, хотя почти все библиотеки реализуют его как последовательный. Вы можете рисковать или использовать вместо него std::vector<char>. Вам нужно будет использовать следующее для добавления:

char str[] = ";charset=";
vector.insert(vector.end(), str, str + sizeof(str) - 1);

std::vector<char> лучше всего подходит для производительности, потому что его, скорее всего, дешевле построить, но он, вероятно, не имеет значения по сравнению с std::string и фактическим временем, которое они берут для построения. Я сделал что-то похожее на то, что вы пытаетесь, и пошел с std::vector<char> раньше. Чисто по логическим причинам; вектор, казалось, лучше соответствовал работе. На самом деле вам не нужны струнные манипуляции или такие. Кроме того, тесты, которые я сделал позже, показали, что он работает лучше или, может быть, только потому, что я не выполнял операции достаточно хорошо с помощью std::string.

При выборе контейнера, который имеет требования к вашим потребностям и минимальным дополнительным функциям, обычно лучше всего работает.

Ответ 2

построение объекта потока является значительно более сложной операцией, чем построение строкового объекта, поскольку оно должно удерживать (и, следовательно, строить) его член std::locale, среди прочего, необходимых для поддержания состояния (но локаль большая разница - самая тяжелая).

Добавление похожее: оба поддерживают непрерывный массив символов, и выделяют больше при превышении емкости. Единственные различия, которые я могу придумать, это то, что при добавлении к потоку есть один вызов функции виртуальной членей для каждого переполнения (в дополнение к распределению/копированию памяти, который в любом случае доминирует над обработкой переполнения), а operator<< должен выполнить некоторые дополнительные проверки состояния потока.

Также обратите внимание, что вы вызываете str(), который копирует всю строку еще раз, поэтому в зависимости от того, что написано вашим кодом, пример потока больше и должен быть медленнее.

Пусть тест:

#include <sstream>
#include <string>
#include <numeric>

volatile unsigned int sink;
std::string contentType(50, ' ');
std::string charset(50, ' ');
int main()
{
 for(long n = 0; n < 10000000; ++n)
 {
#ifdef TEST_STREAM    
    std::ostringstream os;
    os << "Content-Type: " << contentType << ";charset=" << charset << "\r\n";
    std::string header = os.str();
#endif
#ifdef TEST_STRING
    std::string header("Content-Type: ");
    header.append(contentType);
    header.append(";charset=");
    header.append(charset);
    header.append("\r\n");
#endif
    sink += std::accumulate(header.begin(), header.end(), 0);
 }
}

что 10 миллионов повторений

В моей Linux я получаю

                   stream         string
g++ 4.8          7.9 seconds      4.4 seconds
clang++/libc++  11.3 seconds      3.3 seconds

Таким образом, для этого варианта использования в этих двух реализациях строки работают быстрее, но очевидно, что оба способа имеют много возможностей для улучшения (резервировать() строку, вывести конструкцию потока из цикла, использовать поток, который не работает 't требуется копирование для доступа к его буферу и т.д.)

Ответ 3

С потоком вы можете иметь свой класс Myclass переопределить операцию <<, чтобы вы могли писать

MyClass x;
ostringstream y;
y << x;

Для добавления вам нужно иметь метод ToString (или что-то подобное), поскольку вы не можете переопределить функцию добавления строки.

Для некоторых фрагментов кода используйте все, что вам удобно. Используйте поток для больших проектов, где полезно иметь возможность просто передавать объект.

Ответ 4

Если вы беспокоитесь о скорости, вам следует профилировать и/или тестировать. Теоретически std::string::append должен быть не медленнее, поскольку он проще (поток должен иметь дело с языком, другим форматированием и быть более общим). Но как быстрее одно решение или другое действительно вы можете реализовать только при тестировании.