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

Вставка вектора С++ и разность push_back

Я хочу знать, каковы различия между функциями vector push_back и insert.

Есть ли структурные различия?

Есть ли действительно большая разница в производительности?

4b9b3361

Ответ 1

Самая большая разница в их функциональности. push_back всегда помещает новый элемент в конце vector и insert позволяет вам выбрать новую позицию элемента. Это влияет на производительность. Элементы vector перемещаются в память только тогда, когда это необходимо для увеличения длины, потому что для нее было выделено слишком мало памяти. С другой стороны, insert заставляет перемещать все элементы после выбранной позиции нового элемента. Вам просто нужно сделать это для этого. Вот почему insert может быть менее эффективным, чем push_back.

Ответ 2

Функции имеют разные цели. vector::insert позволяет вставить объект в указанную позицию в vector, тогда как vector::push_back будет просто вставлять объект в конец. См. Следующий пример:

using namespace std;
vector<int> v = {1, 3, 4};
v.insert(next(begin(v)), 2);
v.push_back(5);
// v now contains {1, 2, 3, 4, 5}

Вы можете использовать insert для выполнения того же задания, что и push_back с помощью v.insert(v.end(), value).

Ответ 3

Помимо того, что push_back(x) делает то же самое, что и insert(x, end()) (возможно, с несколько лучшей производительностью), есть несколько важных вещей, которые нужно знать об этих функциях:

  1. push_back существует только BackInsertionSequence контейнеров BackInsertionSequence - например, он не существует на set. Это невозможно, потому что push_back() дает вам то, что он всегда будет добавлять в конце.
  2. Некоторые контейнеры также могут удовлетворять FrontInsertionSequence и у них есть push_front. Это удовлетворяется deque, но не vector.
  3. insert(x, ITERATOR) из InsertionSequence, которая является общей для set и vector. Таким образом, вы можете использовать set или vector в качестве цели для нескольких вставок. Тем не менее, set имеет дополнительно insert(x), что делает практически то же самое (эта первая вставка в set означает только ускорение поиска подходящего места, начиная с другого итератора - функция, не используемая в этом случае).

Обратите внимание на последний случай, что если вы собираетесь добавить элементы в цикл, то выполнение container.push_back(x) и container.insert(x, container.end()) фактически сделает то же самое. Однако это не будет правдой, если вы сначала получите этот container.end() а затем используете его во всем цикле.

Например, вы можете рискнуть следующим кодом:

auto pe = v.end();
for (auto& s: a)
    v.insert(pe, v);

Это будет эффективно копировать весь вектор a в вектор v, в обратном порядке, и только если вам повезет, что вы не перераспределите вектор для расширения (вы можете предотвратить это, сначала вызвав reserve()); если вам не так повезет, вы получите так называемый UndefinedBehavior (tm). Теоретически это недопустимо, поскольку векторные итераторы считаются недействительными при каждом добавлении нового элемента.

Если вы делаете это так:

copy(a.begin(), a.end(), back_inserter(v);

он скопирует a в конце v в исходном порядке, и это не несет риска аннулирования итератора.

[РЕДАКТИРОВАТЬ] Ранее я делал так, чтобы этот код выглядел так, и это было ошибкой, потому что фактически inserter поддерживает действительность и продвижение итератора:

copy(a.begin(), a.end(), inserter(v, v.end());

Таким образом, этот код также добавит все элементы в исходном порядке без какого-либо риска.