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

Тип динамического буфера в С++?

Я не совсем новичок в С++, но в прошлом у меня были небольшие серьезные отношения с ним, поэтому мои знания о его возможностях довольно отрывочны.

Я пишу программу быстрого подтверждения концепции на С++, и мне нужен динамически значимый буфер двоичных данных. То есть, я собираюсь получать данные из сетевого сокета, и я не знаю, сколько будет (хотя бы не более нескольких МБ). Я мог бы написать такой буфер сам, но зачем беспокоиться, если у стандартной библиотеки, вероятно, есть что-то уже? Я использую VS2008, поэтому некоторые расширения для Microsoft просто отлично. Мне нужно всего четыре операции:

  • Создать буфер
  • Запись данных в буфер (двоичный мусор, а не завершение нулями)
  • Получить записанные данные как массив char (вместе с его длиной)
  • Бесплатный буфер

Каково имя набора классов/функций/что мне нужно?

Добавлено: Несколько голосов идут в std::vector. Все хорошо и прекрасно, но я не хочу бить несколько Мбайт данных побайтно. Сокет предоставит мне данные в нескольких кубах большого размера, поэтому я бы хотел написать их все сразу. Кроме того, в конце мне нужно будет получить данные как простой char *, потому что мне нужно будет передать весь blob вместе с некоторыми функциями Win32 API без изменений.

4b9b3361

Ответ 1

Вам нужен std::vector:

std::vector<char> myData;

vector автоматически выделяет и освобождает память для вас. Используйте push_back для добавления новых данных (при необходимости vector изменит размер), а оператор индексирования [] для получения данных.

Если в какой-то момент вы можете догадаться, сколько памяти вам понадобится, я предлагаю reserve вызов, чтобы последующий push_back не должен был перераспределять столько же.

Если вы хотите прочитать в куске памяти и добавить его в свой буфер, проще всего было бы что-то вроде:

std::vector<char> myData;
for (;;) {
    const int BufferSize = 1024;
    char rawBuffer[BufferSize];

    const unsigned bytesRead = get_network_data(rawBuffer, sizeof(rawBuffer));
    if (bytesRead <= 0) {
        break;
    }

    myData.insert(myData.end(), rawBuffer, rawBuffer + bytesRead);
}

myData теперь есть все прочитанные данные, чтение фрагмента куском. Однако мы копируем дважды.

Вместо этого мы попробуем что-то вроде этого:

std::vector<char> myData;
for (;;) {
    const int BufferSize = 1024;

    const size_t oldSize = myData.size();
    myData.resize(myData.size() + BufferSize);        

    const unsigned bytesRead = get_network_data(&myData[oldSize], BufferSize);
    myData.resize(oldSize + bytesRead);

    if (bytesRead == 0) {
        break;
    }
}

Который читается непосредственно в буфере за счет чрезмерного распределения.

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

Оба остаются в качестве упражнения для читателя. :)

Наконец, если вам нужно обрабатывать ваши данные как необработанный массив:

some_c_function(myData.data(), myData.size());

std::vector гарантированно будет смежным.

Ответ 2

std::vector<unsigned char> buffer;

Каждый push_back добавит новый char в конец (перераспределение при необходимости). Вы можете вызвать резерв, чтобы свести к минимуму количество распределений, если вы грубо знаете, сколько данных вы ожидаете.

buffer.reserve(1000000);

Если у вас есть что-то вроде этого:

unsigned char buffer[1000];
std::vector<unsigned char> vec(buffer, buffer + 1000);

Ответ 3

std::string будет работать для этого:

  • Он поддерживает встроенные нули.
  • Вы можете добавить к нему многобайтовые фрагменты данных, называя append() на нем указателем и длиной.
  • Вы можете получить его содержимое как массив char, вызвав data() на нем, и текущую длину, вызвав на нем size() или length().
  • Освобождение буфера обрабатывается деструктором автоматически, но вы также можете вызвать clear() на нем, чтобы стереть его содержимое, не уничтожая его.

Ответ 4

Еще один голос за std::vector. Минимальный код, пропускает дополнительный код GMan копии:

std::vector<char> buffer;
static const size_t MaxBytesPerRecv = 1024;
size_t bytesRead;
do
{
    const size_t oldSize = buffer.size();

    buffer.resize(oldSize + MaxBytesPerRecv);
    bytesRead = receive(&buffer[oldSize], MaxBytesPerRecv); // pseudo, as is the case with winsock recv() functions, they get a buffer and maximum bytes to write to the buffer

    myData.resize(oldSize + bytesRead); // shrink the vector, this is practically no-op - it only modifies the internal size, no data is moved/freed
} while (bytesRead > 0);

Как для вызова функций WinAPI - используйте & buffer [0] (да, это немного неуклюже, но так оно и есть), чтобы перейти к аргументам char *, buffer.size() как длина.

И последнее замечание, вы можете использовать std::string вместо std::vector, не должно быть никакой разницы (кроме вы можете писать buffer.data() вместо & buffer [0], если вы буферируете строка)

Ответ 5

Я бы посмотрел Boost basic_streambuf, который предназначен для такого рода целей. Если вы не можете (или не хотите) использовать Boost, я бы рассмотрел std::basic_streambuf, что очень похоже, но немного больше работы для использования. В любом случае вы в основном получаете этот базовый класс и перегружаете underflow() для чтения данных из сокета в буфер. Обычно вы добавляете std::istream в буфер, поэтому другой код читает от него примерно так же, как и пользовательский ввод с клавиатуры (или что-то еще).

Ответ 6

Альтернатива, которая не из STL, но может быть полезной - Boost.Circular buffer

Ответ 7

Используйте std::vector, растущий массив, который гарантирует, что хранилище смежно (ваша третья точка).

Ответ 8

Относительно вашего комментария "Я не вижу append()", а в конце - одно и то же.

vec.insert(vec.end,

Ответ 9

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

Я знаю, это не очень С++ y, но это простая проблема, и другие предложения выглядят как тяжеловесные способы введения ненужной копии.