Как вы копируете содержимое массива в std :: vector в C ++ без зацикливания? - PullRequest
95 голосов
/ 03 ноября 2008

У меня есть массив значений, который передается в мою функцию из другой части программы, которую мне нужно сохранить для дальнейшей обработки. Поскольку я не знаю, сколько раз моя функция будет вызываться до того, как наступит время обработки данных, мне нужна динамическая структура хранения, поэтому я выбрал std::vector. Я не хочу делать стандартный цикл для push_back всех значений по отдельности, было бы хорошо, если бы я мог просто скопировать все это, используя что-то похожее на memcpy.

Ответы [ 10 ]

185 голосов
/ 04 ноября 2008

Здесь было много ответов, и почти все из них выполнят свою работу.

Однако есть один вводящий в заблуждение совет!

Вот варианты:

vector<int> dataVec;

int dataArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
unsigned dataArraySize = sizeof(dataArray) / sizeof(int);

// Method 1: Copy the array to the vector using back_inserter.
{
    copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}

// Method 2: Same as 1 but pre-extend the vector by the size of the array using reserve
{
    dataVec.reserve(dataVec.size() + dataArraySize);
    copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}

// Method 3: Memcpy
{
    dataVec.resize(dataVec.size() + dataArraySize);
    memcpy(&dataVec[dataVec.size() - dataArraySize], &dataArray[0], dataArraySize * sizeof(int));
}

// Method 4: vector::insert
{
    dataVec.insert(dataVec.end(), &dataArray[0], &dataArray[dataArraySize]);
}

// Method 5: vector + vector
{
    vector<int> dataVec2(&dataArray[0], &dataArray[dataArraySize]);
    dataVec.insert(dataVec.end(), dataVec2.begin(), dataVec2.end());
}

Короче говоря, метод 4, использующий vector :: insert, является лучшим для сценария bsruth.

Вот некоторые кровавые подробности:

Метод 1 , вероятно, самый простой для понимания. Просто скопируйте каждый элемент из массива и вставьте его в конец вектора. Увы, это медленно. Поскольку есть цикл (подразумевается функцией копирования), каждый элемент должен обрабатываться индивидуально; улучшения производительности невозможны, поскольку мы знаем, что массив и векторы являются смежными блоками.

Метод 2 является предложенным улучшением производительности по методу 1; просто предварительно зарезервируйте размер массива перед его добавлением. Для больших массивов это может помочь. Однако лучший совет здесь - никогда не использовать резерв, если профилирование не предполагает, что вы можете добиться улучшения (или вам нужно убедиться, что ваши итераторы не будут признаны недействительными). Бьярне соглашается . Кстати, я обнаружил, что этот метод выполнял самый медленный большую часть времени, хотя я изо всех сил пытаюсь всесторонне объяснить, почему он регулярно значительно медленнее, чем метод 1 ...

Метод 3 - это старое школьное решение - бросьте немного C в проблему! Работает нормально и быстро для типов POD. В этом случае необходимо вызывать изменение размера, так как memcpy работает за пределами вектора, и нет никакого способа сообщить вектору, что его размер изменился. Помимо того, что это уродливое решение (байтовое копирование!), Помните, что это может использоваться только для типов POD . Я бы никогда не использовал это решение.

Метод 4 - лучший путь. Смысл понятен, он (обычно) самый быстрый и работает для любых объектов. Нет недостатка в использовании этого метода для этого приложения.

Метод 5 - это настройка метода 4 - скопируйте массив в вектор, а затем добавьте его. Хороший вариант - как правило, быстрый и понятный.

Наконец, вы знаете, что вы можете использовать векторы вместо массивов, верно? Даже когда функция ожидает массивы в стиле c, вы можете использовать векторы:

vector<char> v(50); // Ensure there's enough space
strcpy(&v[0], "prefer vectors to c arrays");

Надеюсь, это поможет кому-то там!

96 голосов
/ 03 ноября 2008

Если вы можете построить вектор после того, как вы получили массив и размер массива, вы можете просто сказать:

std::vector<ValueType> vec(a, a + n);

... при условии, что a это ваш массив, а n это количество элементов, которые он содержит. В противном случае, std::copy() w / resize() добьется цели.

Я бы держался подальше от memcpy(), если только вы не можете быть уверены, что значения относятся к типам старых данных (POD).

Кроме того, стоит отметить, что ни один из них действительно не избегает цикла for - это просто вопрос того, должны ли вы видеть это в своем коде или нет. O (n) производительность во время выполнения неизбежна для копирования значений.

Наконец, обратите внимание, что массивы в стиле C являются совершенно допустимыми контейнерами для большинства алгоритмов STL - необработанный указатель эквивалентен begin(), а (ptr + n) эквивалентен end().

28 голосов
/ 03 ноября 2008

Если все, что вы делаете, это замена существующих данных, то вы можете сделать это

std::vector<int> data; // evil global :)

void CopyData(int *newData, size_t count)
{
   data.assign(newData, newData + count);
}
11 голосов
/ 03 ноября 2008

std :: copy - это то, что вы ищете.

4 голосов
/ 01 марта 2017
int dataArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//source

unsigned dataArraySize = sizeof(dataArray) / sizeof(int);

std::vector<int> myvector (dataArraySize );//target

std::copy ( myints, myints+dataArraySize , myvector.begin() );

//myvector now has 1,2,3,...10 :-)
4 голосов
/ 03 ноября 2008

Поскольку я могу редактировать только свой собственный ответ, я собираюсь составить ответ из других ответов на мой вопрос. Спасибо всем, кто ответил.

Используя std :: copy , это все еще выполняется в фоновом режиме, но вам не нужно вводить код.

int foo(int* data, int size)
{
   static std::vector<int> my_data; //normally a class variable
   std::copy(data, data + size, std::back_inserter(my_data));
   return 0;
}

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

vector<int> x(size);
memcpy(&x[0], source, size*sizeof(int));
3 голосов
/ 03 ноября 2008

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

1 голос
/ 03 ноября 2008

Еще один ответ, поскольку человек сказал: «Я не знаю, сколько раз будет вызываться моя функция», вы можете использовать метод вставки вектора, например, так, чтобы добавлять массивы значений в конец вектора:

vector<int> x;

void AddValues(int* values, size_t size)
{
   x.insert(x.end(), values, values+size);
}

Мне нравится этот способ, потому что реализация вектора должна быть в состоянии оптимизировать для наилучшего способа вставки значений на основе типа итератора и самого типа. Вы несколько отвечаете на реализацию stl.

Если вам нужно гарантировать максимальную скорость, и вы знаете, что ваш тип является типом POD, я бы порекомендовал метод изменения размера в ответе Томаса:

vector<int> x;

void AddValues(int* values, size_t size)
{
   size_t old_size(x.size());
   x.resize(old_size + size, 0);
   memcpy(&x[old_size], values, size * sizeof(int));
}
1 голос
/ 03 ноября 2008

В дополнение к методам, представленным выше, вам необходимо убедиться, что вы используете либо std :: Vector.reserve (), std :: Vector.resize (), либо создаете вектор по размеру, чтобы убедиться, что ваш вектор имеет в нем достаточно элементов для хранения ваших данных. если нет, вы испортите память. Это верно как для std :: copy (), так и для memcpy ().

Это причина для использования vector.push_back (), вы не можете писать после конца вектора.

0 голосов
/ 03 ноября 2008

Предполагая, что вы знаете, насколько велик элемент в векторе:

std::vector<int> myArray;
myArray.resize (item_count, 0);
memcpy (&myArray.front(), source, item_count * sizeof(int));

http://www.cppreference.com/wiki/stl/vector/start

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...