Размещение контейнеров STL новое

Точного ответа на этот вопрос я не нашел, поэтому и пишу здесь. Когда я думаю о векторе, он должен создавать объекты в непрерывной области памяти. Это означает, что вектор сохраняет выделенную память и должен выполнять построение на месте (= размещение нового) объектов, помещаемых в него. Верно ли это предположение? Кроме того, означает ли это, что контейнер вручную вызывает деструктор, а не вызывает удаление? Есть ли какие-либо другие предположения, которые мне здесь не хватает? Означает ли это, что я могу предположить, что даже специально написанное новое для объекта не может быть вызвано, если я решу написать?

Также для списка имеет смысл использовать new и delete, поскольку нам не нужна гарантия непрерывной памяти. Итак, является ли такое поведение тем, что определяет поведение аллокаторов? Пожалуйста помоги. Спасибо


person Kiran    schedule 14.03.2011    source источник


Ответы (2)


Это означает, что вектор сохраняет выделенную память и должен выполнять построение на месте (= размещение нового) объектов, помещаемых в него. Верно ли это предположение?

да

Кроме того, означает ли это, что контейнер вручную вызывает деструктор, а не вызывает удаление?

да

Есть ли какие-либо другие предположения, которые мне здесь не хватает? Означает ли это, что я могу предположить, что даже специально написанное новое для объекта не может быть вызвано, если я решу написать?

да. Учтите, что даже в связанных списках контейнер будет выделять не экземпляр вашего типа, а шаблонную структуру, содержащую подобъект типа. Для связанного списка это будет некоторый сложный тип, содержащий как минимум два указателя (обе ссылки) и подобъект вашего типа. На самом деле выделяемый тип — это node, а не ваш тип.

Также для списка имеет смысл использовать new и delete, поскольку нам не нужна гарантия непрерывной памяти.

Да, но не new/delete объекты вашего типа.

Итак, является ли такое поведение тем, что определяет поведение аллокаторов?

Я не очень понимаю эту часть вопроса. Распределители — это классы, которые имеют набор ограничений, определенных в стандарте, которые включают как интерфейс (allocate, deallocate...), так и семантику (значение == заключается в том, что память, выделенная для одного, может быть освобождена другим, любое другое состояние в классе не имеет значения).

Распределители могут быть созданы и переданы в контейнеры по разным причинам, включая эффективность (если вы выделяете только тип объекта, вы можете реализовать небольшие распределители блоков немного более эффективно, чем malloc - или нет, зависит от ситуации) .

Примечание о новых местах размещения

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

На самом деле существует единственное значение placement new, которое не имеет ничего общего с созданием in-place. Первый — это просто случай второго, когда распределитель предоставляется реализацией (компилятором), как определено в 18.4.1.3, и не может быть перегружен. Эта конкретная версия перегруженного распределителя абсолютно ничего не делает, кроме как возвращает аргумент (void*), чтобы новое выражение могло передать его в конструктор и создать объект в памяти (не), выделенной < em>разместить новую версию, которая была вызвана.

person David Rodríguez - dribeas    schedule 14.03.2011

Вы очень близки к тому, чтобы быть совершенно правильным. Способ, которым vector (и все другие стандартные контейнеры) выполняют свое распределение, заключается в использовании класса std::allocator, который поддерживает создание и уничтожение объектов в определенных местах. Внутренне это использует размещение new и явные вызовы деструктора для установки и уничтожения объектов.

Причина, по которой я говорю «очень близко к совершенно правильному», заключается в том, что можно настроить способ, которым контейнеры STL получают свою память, предоставив новый распределитель в качестве аргумента шаблона вместо значения по умолчанию. Это означает, что теоретически контейнеры STL должны иметь возможность создавать и уничтожать объекты разными способами, хотя по умолчанию они будут использовать стандартное размещение new.

person templatetypedef    schedule 14.03.2011
comment
Первый абзац правильный, второй нет. Контейнеры должны использовать распределители, предоставленные в качестве аргумента шаблона, и использовать размещение new в памяти, выделенной через них. Контейнеры STL не могут создавать и уничтожать по-разному, они должны получать память из распределителя и должны конструировать путем вызова new размещения с аргументом void*, так как это единственный способ создания объекта, который не подразумевает выделение памяти. - person David Rodríguez - dribeas; 15.03.2011
comment
@David Rodriguez: я думаю, что контейнеры внутри используют конструкцию распределителя и разрушают функции-члены, которые IIRC не требуется для использования нового размещения. Я не понимаю, как еще они могли бы это сделать без использования специфичной для компилятора функции, но теоретически они могли бы. Пожалуйста, поправьте меня, если я ошибаюсь в этом. - person templatetypedef; 15.03.2011
comment
В текущем стандарте на странице 362, раздел §20.1.5 [lib.allocator.requirements] есть таблица 32, содержащая требования к распределителю. Последние две строки таблицы: a.construct(p,t), (не используется), Effect: new ((void*)p) T(t) и a. destroy(p), (не используется), Эффект: ((T)p)-›~T()*, где в средней колонке четко указано, что эти два метода не используются . Зачем они нужны в интерфейсе ускользает от меня - person David Rodríguez - dribeas; 15.03.2011