Что происходит под капотом vector:: push back memory wise?
мой вопрос касается эффекта vector::push_back
, Я знаю, что он добавляет элемент в конец вектора, но то, что происходит под капотом?
объекты памяти IIRC выделяются последовательно, поэтому мой вопрос заключается в том,vector::push_back
просто выделяет больше памяти сразу после вектора, и если да, то что произойдет, если в этом месте недостаточно свободной памяти? Или, возможно, указатель добавляется в "конец", чтобы заставить вектор" прыгать " в место, где он продолжается? Или он просто перераспределяется путем копирования в другое место, где достаточно места, и старая копия отбрасывается? Или что-то еще?
7 ответов
если уже выделено достаточно места, объект копируется из аргумента на месте. Когда недостаточно памяти, вектор будет расти, это внутренний databuffer после какой-то геометрической прогрессии (каждый раз, когда новый размер будет k*old_size
с k > 1
[1]) и все объекты, присутствующие в исходном буфере, будут тогда двигался к новому буферу. После завершения операции старый буфер будет освобожден для система.
в предыдущем предложении движение не используется в технической перемещение-конструктор/ переместить-задание смысле, они могут быть двигался или скопировал или любая эквивалентная операция.
[1] растет в разы k > 1
гарантирует, что амортизированная стоимость push_back
- это константа. Фактическая константа варьируется от одной реализации к другой (Dinkumware использует 1.5, gcc использует 2). Этот амортизированной стоимости означает, что даже если каждый так часто, один push_back
будет очень дорого (O(N)
о размере вектора в то время), эти случаи происходят достаточно редко, что стоимость всех операций над всем набором вставок линейна по количеству вставок, и, следовательно, каждая вставка средние постоянная стоимость)
когда вектор находится вне пространства, он будет использовать его распределитель, чтобы зарезервировать больше места.
это до распределителя, чтобы решить, как это реализуется.
однако вектор решает, сколько места резервировать: стандарт гарантирует, что емкость вектора должна расти, по крайней мере, в 1,5 раза1геометрически (см. комментарий), тем самым предотвращая ужасную производительность из-за повторных "небольших" распределений.
на физическом перемещение / копирование элементов:
- в C++11, соответствующей реализации движение элементы, если они поддерживают назначение перемещения и строительство
- большинство реализаций, которые я знаю (особенно g++), будут просто использовать std::copy для типов POD; специализация алгоритма для типов POD гарантирует, что это компилируется в (по существу) операцию memcpy. Это, в свою очередь, компилируется в любой инструкции CPU быстрее всего в вашей системе (например, SSE2 инструкции)
1 Я попытался найти справочную цитату для этого из стандартного проекта документа n3242, но я не смог найти ее в это время
вектор гарантирует, что все элементы смежны в памяти.
внутренне вы можете думать об этом как о трех указателях (или что действует как указатели):
start: Points at the beginning of the allocated block.
final: Points one past the last element in the vector.
If the vector is empty then start == final
capacity: Points one past the end of allocated memory.
If final == capacity there is no room left.
когда вы нажимаете назад.
- если final меньше емкости:
- новый элемент копируется в место, указанное final
- final увеличивается до следующего местоположения.
- если final совпадает с емкостью тогда вектор заполнен
- необходимо выделить новую память.
- компилятор затем выделит
X*(capacity - start)*sizeof(t)
байт. - где X-обычно значение от 1,5 до 2.
- затем он копирует все значения из старого буфера в новый буфер памяти.
- новое значение добавляется в буфер.
- переносит указатели start/final/capacity.
- освободите старый буфер
, когда vector
заканчивается пространство, оно перераспределяется, и все элементы копируются в новый массив. Затем старый массив уничтожается.
чтобы избежать чрезмерного количества ассигнований и сохранить среднее значение push_back()
времени O(1)
, перераспределение требует, чтобы размер был увеличен по крайней мере постоянным фактором. (1.5 и 2 являются общими)
когда вы называете vector::push_back
на конец указатель сравнивается с емкости указатель. Если есть достаточно места для нового объекта placement new
вызывается для построения объекта в доступном пространстве и конец указатель увеличивается на единицу.
Если не хватает места vector
называет выделения выделить достаточно смежного пространства, по крайней мере, для существующих элементов плюс новый элемент (может расти другая реализация выделенная память разными множителями). Тогда все существующие элементы плюс новый копируются во вновь выделенное пространство.
std:: vector overallocates - он обычно выделяет больше памяти, чем необходимо автоматически. size
не аффектд этим, но вы можете контролировать это через capacity
.
std:: vector скопирует все если дополнительная емкость недостаточна.
память, выделенная std:: vector, является raw, никакие конструкторы не вызываются по требованию, используя размещение new.
Так, push_back тут:
- если емкость не достаточна для нового элемента, она будет
- выделить новый блок
- копировать все существующие элементы (обычно с помощью конструктора копирования)
- увеличить размер на один
- скопируйте новый элемент в новое место
если у вас есть представление о том, каким будет конечный размер вашего массива, попробуйте vector::reserve
память сначала. Обратите внимание, что reserve
отличается от vector::resize
. С reserve
на vector::size()
вашего массива не изменяется