Я хочу знать, каковы различия между функциями vector
push_back
и insert
.
Есть ли структурные различия?
Есть ли действительно большая разница в производительности?
Я хочу знать, каковы различия между функциями vector
push_back
и insert
.
Есть ли структурные различия?
Есть ли действительно большая разница в производительности?
Самая большая разница в их функциональности. push_back
всегда помещает новый элемент в конце vector
и insert
позволяет вам выбрать новую позицию элемента. Это влияет на производительность. Элементы vector
перемещаются в память только тогда, когда это необходимо для увеличения длины, потому что для нее было выделено слишком мало памяти. С другой стороны, insert
заставляет перемещать все элементы после выбранной позиции нового элемента. Вам просто нужно сделать это для этого. Вот почему insert
может быть менее эффективным, чем push_back
.
Функции имеют разные цели. 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)
.
Помимо того, что push_back(x)
делает то же самое, что и insert(x, end())
(возможно, с несколько лучшей производительностью), есть несколько важных вещей, которые нужно знать об этих функциях:
push_back
существует только BackInsertionSequence
контейнеров BackInsertionSequence
- например, он не существует на set
. Это невозможно, потому что push_back()
дает вам то, что он всегда будет добавлять в конце.FrontInsertionSequence
и у них есть push_front
. Это удовлетворяется deque
, но не vector
.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());
Таким образом, этот код также добавит все элементы в исходном порядке без какого-либо риска.