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

Какое преимущество использования std:: allocator вместо нового в С++?

Я только что прочитал о std::allocator. По-моему, сложнее использовать его вместо использования new и delete.

С allocator мы должны явно выделить кучу памяти, построить ее, уничтожить, а затем окончательно освободить память. Так почему это было создано?

В каких случаях его можно использовать и когда его следует использовать вместо нового и удалить?

4b9b3361

Ответ 1

std::allocator - это распределитель памяти по умолчанию для стандартных контейнеров библиотеки, и вы можете заменить свои собственные распределители. Это позволяет вам контролировать, как стандартные контейнеры распределяют память. Но я не думаю, что ваш вопрос касается конкретно std::allocator, а скорее стратегии выделения памяти, а затем создания объектов в этой памяти вместо использования new T[N], например.

И причина в том, что new T[N] не позволяет вам контролировать, какие конструкторы вызываются. И это заставляет вас строить все ваши объекты одновременно. Это ужасно для целей, например, std::vector, где вы хотите иногда выделять.

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

Затем, когда у вас заканчивается память, вы выделяете больше, обычно в два раза больше. Если std::vector используется new T[N], ему придется перераспределять каждый раз, когда вы хотите добавить или удалить элемент, что было бы ужасно для производительности. Вы также должны были бы использовать конструктор по умолчанию для всех объектов, что может привести к ненужному ограничению типов объектов std::vector.

Ответ 2

По-моему, сложнее использовать его вместо использования new и delete.

Да, но он не предназначен для замены new и delete, он выполняет другую цель.

С распределителем мы должны явно выделять кучную память, строить ее, уничтожать, а затем, наконец, освобождать память.

Так почему это было создано?

Потому что иногда вы хотите разделить распределение и построение на два этапа (и аналогично отдельному уничтожению и освобождению на два этапа). Если вы не хотите этого делать, не используйте распределитель, используйте new вместо этого.

В каких случаях его можно использовать и когда его следует использовать вместо нового и удалить?

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

Рассмотрим следующий код:

std::vector<X> v;
v.reserve(4);        // (1)
v.push_back( X{} );  // (2)
v.push_back( X{} );  // (3)
v.clear();           // (4)

Здесь строка (1) должна выделять достаточно памяти для четырех объектов, но еще не создавать их. Затем строки (2) и (3) должны создавать объекты в выделенной памяти. Затем строка (4) должна уничтожать эти объекты, но не освобождать память. Наконец, в векторном деструкторе вся память может быть освобождена.

Таким образом, вектор не может просто использовать new X() или delete &m_data[1] для создания и уничтожения объектов, он должен выполнять выделение/освобождение отдельно от конструкции/уничтожения. Аргумент шаблона-распределителя контейнера определяет политику, которая должна использоваться для (де) распределения памяти и построения/уничтожения объектов, что позволяет настраивать использование памяти в контейнере. По умолчанию используется тип std::allocator.

Таким образом, вы используете распределитель, когда требуется распределитель (например, при использовании контейнера), и вы используете std::allocator, когда вы не хотите предоставлять настраиваемый распределитель и просто хотите использовать стандартный.

Вы не используете распределитель в качестве замены для new и delete.

Ответ 3

Активаторы - очень важная концепция в STL. Каждый контейнер способен принимать распределитель в качестве аргумента. Затем распределения будут выполняться с использованием этого распределителя, а не стандартного.

Это полезно, например, для выделения объектов одинакового размера в пуле, для повышения производительности или может потребоваться, если имеется специальная область памяти, в которой должны жить ваши объекты.

Шаги выделения и конструирования являются отдельными, поскольку, например, для вектора (std::vector::reserve) важно иметь возможность выделять память для будущего использования, но не создавать (там) объекты там.

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

См. больше примеров здесь в этом сообщении SO.

[...], когда он должен использоваться [...]

Если у вас есть особые потребности и наиболее важно при написании собственных универсальных контейнеров.

Ответ 4

Твой инстинкт прав. В 90% случаев используйте new. Однако обратите внимание на структуры, такие как, например, структура данных map. Одним из аргументов шаблона по умолчанию является class Alloc = allocator<pair<const Key,T>, который определяет, как класс создает новые экземпляры вещей и управляет существующими экземплярами. Таким образом, вы можете теоретически создать свой собственный распределитель, а затем использовать его для существующих структур данных. Поскольку new и delete являются функциями, а не классами, необходимо, чтобы std::allocator представлял их и делал их допустимыми аргументами шаблона.

Ответ 5

std::allocator был создан, чтобы позволить разработчикам больше контролировать распределение памяти. Во многих встроенных системах память ограничена и имеет разные типы. Там не может быть огромной суммы. Кроме того, выделение памяти необходимо минимизировать, чтобы избежать проблем с фрагментацией.

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

Ответ 6

Причиной этого члена STL является предоставление разработчику большего контроля над памятью. Я имею в виду, например, что новый оператор - это не просто одна операция как таковая. В самом основном, он выполняет резервирование памяти И затем заполняет это пространство объектом.

Хотя я не могу на вершине головы придумать конкретный сценарий реального мира, вы должны использовать std::allocator и такие, когда, возможно, уничтожение данного объекта может повлиять на другие объекты в памяти.

Скажем, ради аргумента, вы создали какой-то вектор, который каждый элемент дважды связан с каким-либо другим объектом в памяти, и вы хотите, чтобы во время удаления указанного вектора объекты, связанные с удалением ссылайтесь на него.

Ответ 7

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

С помощью распределителя мы должны явно выделять кучную память, строить ее, уничтожить его, а затем окончательно освободить память.

Действительно, распределители не должны использоваться для "нормального" кода, где new и delete будут одинаково хорошими. Рассмотрим класс типа std::map, который часто реализуется как дерево: нужно ли освобождать весь лист всякий раз, когда удаленный объект удаляется? Распределители позволяют вам уничтожить этот объект, но сохраните память, чтобы вам не пришлось требовать ее снова.

Кроме того, вы можете специализировать распределитель для определенного типа, если знаете более оптимизированные методы его управления, что невозможно для new и delete.

Ответ 8

Ты смущен. std::allocator вызывает/использует new и delete. Это просто еще один уровень в иерархии памяти C++, используемый для удовлетворения различных потребностей стандартной библиотеки C++, в частности контейнеров, но и других типов. Контейнеры библиотеки C++ используют распределитель для автоматического управления памятью содержащихся элементов. Без этого все было бы более громоздким и, следовательно, более сложным в использовании. Кроме того, распределитель может использоваться для выполнения различных методов управления памятью, например, выделение стека, линейное выделение, выделение кучи, выделение пула и т.д.

C++ память "иерархия"

_________________
|Applications   |
|_______________|
      |
______↓_______________________
|C++ library (std::allocator)|
|____________________________|
      |
______↓______________________________________________________________________________
|C++ primitives (new/delete, new[]/delete[], ::operator new()/::operator delete())  |
|___________________________________________________________________________________|
      |
______↓______
|malloc/free|
|___________|
      |
______↓______________
|OS APIs, syscalls  |
|___________________|

Это нормальный поток вызовов, но вместо этого приложение может напрямую вызывать malloc/free, new/delete или даже API-интерфейсы ОС. Вы видите это ВСЕ абстракция. Приведенный выше уровень абстрагирует более сложную природу и упаковывает ее в более простой в использовании пакет.