Что происходит под капотом векторной:: push_back памяти мудрым?

Мой вопрос касается эффекта vector::push_back, я знаю, что он добавляет элемент в конец вектора, но что происходит под капотом?

Объекты памяти IIRC выделяются последовательным образом, поэтому мой вопрос заключается в том, что vector::push_back просто выделяет больше памяти сразу после вектора, и если да, то что произойдет, если в этом месте недостаточно свободной памяти? Или, возможно, указатель добавлен в "конец", чтобы заставить вектор "перескакивать" по месту его продолжения? Или он просто перераспределяется путем копирования его в другое место, где достаточно места, и старая копия отбрасывается? Или может быть что-то еще?

Ответ 1

Если уже выделено достаточно места, объект будет скопирован из аргумента на месте. Когда памяти недостаточно, вектор будет вырабатывать внутренний databuffer после некоторой геометрической прогрессии (каждый раз новый размер будет k*old_size с k > 1 [1]) и все объекты, присутствующие в исходном буфере будет перенесен в новый буфер. По завершении операции старый буфер будет выпущен в систему.

В предыдущем предложении перемещение не используется в техническом переводе-конструкторе/перемещении-присваивании, они могут быть перемещены или скопированы или любая эквивалентная операция.

[1] Рост с коэффициентом k > 1 гарантирует, что амортизированная стоимость push_back является постоянной. Фактическая константа варьируется от одной реализации к другой (Dinkumware использует 1.5, gcc использует 2). Амортизированная стоимость означает, что даже если каждый так называемый push_back будет очень дорогим (O(N) по размеру вектора в то время), эти случаи случаются достаточно редко, что стоимость всех операций над всем набором вставок линейна по числу вставок, и, следовательно, каждая вставка усредняет постоянную стоимость)

Ответ 2

Когда вектор выходит за пределы пространства, он будет использовать его распределитель, чтобы зарезервировать больше места.

Репликатор должен решить, как это реализовать.

Однако вектор решает, сколько места зарезервировать: стандарт гарантирует, что векторная емкость будет расти по крайней мере в 1,5 раза 1 геометрически (см. комментарий), тем самым предотвращая ужасную производительность из-за повторяющиеся "маленькие" распределения.

О физическом перемещении/копировании элементов:

  • С++ 11 соответствующие реализации будут перемещать элементы, если они поддерживают назначение и конструкцию перемещения
  • большинство реализаций, которые я знаю (особенно g++), будут просто использовать std:: copy для типов POD; специализация алгоритма для типов POD гарантирует, что это скомпилирует (по существу) операцию memcpy. Это, в свою очередь, скомпилируется в любой инструкции процессора, самой быстрой в вашей системе (например, инструкции SSE2).

1 Я попытался найти ссылочную цитату из стандартного документа проекта n3242, но я не смог найти его в это время

Ответ 3

Когда vector пробегает пробел, он перераспределяется, и все элементы копируются в новый массив. Затем уничтожается старый массив.

Чтобы избежать чрезмерного количества распределений и сохранить среднее время push_back() при O(1), для перераспределения требуется, чтобы размер был увеличен, по меньшей мере, на постоянный коэффициент. (1,5 и 2 являются общими)

Ответ 4

Вектор гарантирует, что все элементы в памяти свободны.

Внутри вы можете думать о нем как о трех указателях (или о том, что действует как указатели):

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 совпадает с емкостью, тогда вектор заполняется
    • должна быть назначена новая память.
    • Затем компилятор выделяет X*(capacity - start)*sizeof(t) байты.
    • где X обычно имеет значение от 1,5 до 2.
    • Затем он копирует все значения из старого буфера памяти в новый буфер памяти.
    • новое значение добавляется в буфер.
    • Перенос начальных/конечных/пропускных пунктов передачи.
    • Освободите старый буфер

Ответ 5

При вызове vector::push_back конечный указатель сравнивается с указателем емкости. Если достаточно места для нового объекта placement new вызывается для создания объекта в доступном пространстве, а конечный указатель увеличивается.

Если не хватает места, vector вызывает свой распределитель для распределения достаточного смежного пространства для по крайней мере существующих элементов плюс новый элемент (другая реализация может увеличить выделенную память разными множителями). Затем все существующие элементы плюс новый копируются в новое выделенное пространство.

Ответ 6

std::vector globalocates - он обычно выделяет больше памяти, чем необходимо автоматически. size не влияет на это, но вы можете контролировать это через capacity.

std::vector скопирует все, если дополнительной емкости недостаточно.

Память, выделенная std::vector, является исходной, никакие конструкторы не вызываются по требованию, используя размещение new.

Итак, push_back делает:

  • Если для нового элемента недостаточно емкости, это будет
    • выделить новый блок
    • скопировать все существующие элементы (обычно используя конструктор копирования)
  • увеличить размер на один
  • скопируйте новый элемент в новое место

Ответ 7

Если у вас есть представление о том, каков будет конечный размер вашего массива, сначала попробуйте vector::reserve память. Обратите внимание, что reserve отличается от vector::resize. При reserve vector::size() вашего массива не изменяется