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



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

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

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

150   7  

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::allocator to представьте их и сделайте их допустимыми аргументами шаблона.

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

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

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

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

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

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

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

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

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

    Ничего не найдено.

Добавить ответ:
Отменить.