Что такое преимущество использования std :: allocator вместо new в C ++?

Я только что прочитал о std::allocator, На мой взгляд, это сложнее использовать вместо того, чтобы использовать new а также delete,

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

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

56

Решение

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

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

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

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

41

Другие решения

На мой взгляд, сложнее использовать его вместо использования new и delete.

Да, но это не значит заменить new а также delete, это служит другой цели.

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

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

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

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

Когда вам нужно поведение распределителя, а не поведение 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,

47

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

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

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

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

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

[…] когда это должно использоваться […]

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

8

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

6

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

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

5

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

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

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

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

4

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

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

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

4