В чем преимущество использования std::allocator вместо new В C++?
Я только что прочитал о std::allocator. На мой взгляд, это сложнее использовать его вместо использования new и delete.
С allocator мы должны явно выделить память кучи, построить ее, уничтожить ее, а затем, наконец, освободить память. Так почему же он был создан?
в каких случаях он может быть использован и когда он должен быть использован вместо нового и удалить?
7 ответов:
std::allocatorявляется распределителем памяти по умолчанию для стандартных контейнеров библиотеки, и вы можете заменить свои собственные распределители. Это позволяет управлять тем, как стандартные контейнеры выделяют память. Но я не думаю, что ваш вопрос оstd::allocatorв частности, но скорее стратегия выделения памяти, а затем построение объектов в этой памяти, а не использованиеnew T[N], например.и причина этого в том, что
new T[N]не позволяет вам контролировать то, что конструкторы называются. И это заставляет вас строить все объекты одновременно. Это ужасно для целей, например,std::vectorгде вы только хотите выделить иногда.С помощью распределителя необработанной памяти вы можете выделить определенный объем памяти, который определяет вашу емкость. Затем, когда пользователь добавляет элементы в вектор (используя конструктор по своему выбору), вы можете создавать объекты на месте в этой памяти.
затем, когда вы бежите из память вы выделяете больше, обычно в два раза больше. Если
std::vectorиспользуетсяnew T[N], он должен будет перераспределять каждый раз, когда вы хотите добавить или удалить элемент, что было бы ужасно для производительности. Вы также будете вынуждены использовать конструктор по умолчанию для всех объектов, что накладывает ненужное ограничение на типы объектовstd::vectorможно провести.
на мой взгляд, это сложнее, чтобы использовать его вместо использования New и delete.
да, но он не предназначен для замены
newиdelete, оно служит другой цели.С помощью распределителя мы должны явно выделить память кучи, построить ее, уничтожить ее, а затем, наконец, освободить память.
так почему же он был создан?
потому что иногда вы хотите отделить выделение и строительство в два этапа (и аналогично разделению уничтожения и освобождения на два этапа). Если вы не хотите этого делать, не используйте распределитель, используйте
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]для создания и уничтожения объектов, он должен выполнять выделение/освобождение отдельно от строительства / уничтожения. Аргумент шаблона распределителя контейнера определяет политику, которая должна использоваться для (de)выделения памяти и построения/разрушения объектов, позволяя настраивать использование памяти контейнера. Политика по умолчанию -std::allocatorтип.таким образом, вы используете распределитель, когда требуется распределитель (например, при использовании контейнера), и вы используете
std::allocatorкогда вы не хотите предоставлять пользовательский распределитель и просто хочу стандартный.вы не используете распределитель в качестве замены
newиdelete.
распределители являются очень важным понятием в STL. Каждый контейнер способен принимать распределитель в качестве аргумента. Затем распределения будут выполняться с использованием этого распределителя, а не стандартного.
это полезно, например, для выделения объектов одинакового размера в пуле, для повышения производительности или может потребоваться, если есть специальная область памяти, где ваши объекты должны жить.
шаги выделения и построения являются отдельными, потому что, например, для вектор (
std::vector::reserve) важно иметь возможность выделять память для будущего использования, но не (пока) создавать в ней объекты.Как пример вы можете написать распределитель как класс, содержащий массив фиксированного размера, и использовать этот массив для предоставления памяти для некоторого стандартного контейнера. Затем вы можете иметь экземпляр этого класса в стеке и, таким образом, полностью избежать выделения кучи для некоторой части вашей программы.
Посмотреть больше примеров вот в этом посте.
[...] когда он должен быть использован [...]
Если у вас есть конкретные потребности, и самое важное при написании собственных универсальных контейнеров.
ваш инстинкт прав. В 90% случаев, используйте
new. Однако обратите внимание на такие структуры, как, скажем,карта структуры данных. Одним из аргументов шаблона по умолчанию являетсяclass Alloc = allocator<pair<const Key,T>, который определяет, как класс создает новые экземпляры вещей и управляет существующими экземплярами. Таким образом, теоретически можно создать свой собственный распределитель, а затем использовать его для существующих структур данных. Так какnewиdeleteявляются функциями, а не классами, необходимо иметьstd::allocatorto представьте их и сделайте их допустимыми аргументами шаблона.
на
std::allocatorбыл создан, чтобы позволить разработчикам больше контроля над тем, как выделяется память. Во многих встроенных системах память ограничена и в разных типах. Там не может быть огромное количество. Кроме того, выделение памяти должно быть сведено к минимуму, чтобы избежать проблем фрагментации.распределитель также позволяет выполнять выделение из различных пулов памяти. Так, например, выделение небольших блоков было бы более эффективным из небольшого пула памяти блоков.
newиdeleteэто прямой способ создать объект в динамической памяти и инициализировать его. Распределители гораздо больше, хотя, потому что они предлагают полный контроль над вышеупомянутыми фазами.С помощью распределителя мы должны явно выделить память кучи, построить ее, уничтожьте его, а затем, наконец, освободите память.
действительно распределители не должны использоваться для "нормального" кода, где
newиdeleteтак же будет штраф. Рассмотрим такой класс, какstd::map, часто реализуется как дерево: вам нужно освободить весь лист всякий раз, когда объект, удерживаемый удаляется? Распределители позволяют вам уничтожить этот объект, но сохранить память, чтобы вам не пришлось требовать ее снова.кроме того, вы можете специализировать распределитель для определенного типа, если вы знаете более оптимизированные методы для его управления, что невозможно для
newиdelete.
причина этого STL - член, чтобы дать разработчику больше контроля над памятью. Я имею в виду, например, что новый оператор-это не просто одна операция как таковая. В своей самой простой форме он выполняет резервирование памяти, а затем заполняет это пространство объектом.
хотя я не могу в голове придумать конкретный, реальный сценарий, вы должны использовать
std::allocatorи такие, когда, возможно, разрушение данного объекта может воздействуйте на другие объекты в памяти.допустим, ради аргумента вы создали какой-то вектор, каждый элемент которого дважды связан с каким-то другим объектом в памяти, и вы хотите, чтобы во время удаления указанного вектора объекты, связанные с удалением ссылки обратно на него.
Comments