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

container -> allocator_traits -> allocator -> operator new -> malloc -> OS

последним параметром в стандартных контейнерах - аллокатор, по умолчанию = std::allocator<T>. при любой работе с памятью используются функции аллокатора (не напрямую, а через allocator_traits). хранение аллокатора занимает 0 байт. (empty base optimization EBO) если нужно аллоцировать память на элементы типа не Т (например ноды в std::list), то хранится как поле typename Alloc:: template rebind<Node>::other - шаблонный юзинг (метафункция), позволяющий аллоцировать другие типы

все стандартные аллокаторы == друг другу а так, считаются равными, если одним можно удалить то, что было выделено другим

идеи нестандартных аллокаторов:

  • PoolAllocator - изначально выделяет большй массив памяти и им распоряжается
  • StackAllocator - аллокатор, который вообще к new не обращается, а всё создает на стеке
  • FreeLostAllocator - изначально выделяет ноды, а вместо освобождения складывает в лист свободных нод и отдаёт, если запросят новые

смысл аллокаторов в том, чтобы полностью отделить контейнеры от вызова new

почему template<typename Alloc = std::allocator<T>>, а не template <typename> Alloc = std::allocator, т.е. почему аллокатор не может быть шаблонным шаблонным параметром? потому что у аллокаторов могут быть разные параметры, даже не всегда типы

allocator_traits - набор метафункций - шаблонная структура, которая для всех аллокаторов предопределяет некоторые функции и некоторые using’и, чтоб не нужно было писать их заново

allocator-aware контейнер - это контейнер, который держит в себе объект аллокатора и использует его для работы с памятью. можно использовать в нём кастомный аллокатор все STL контейнеры кроме std::array и std::inplace_vector - AllocatorAware метод (либо аллокатора, либо из allocator_traits) select_on_container_copy_construction() - возвращает аллокатор либо копию аллокатора, либо новый аллокатор созданный по умолчанию для присваивания есть propogate_on_container_copy_assignment - если в метафункции copyassignable std::truetype, то присваиваем, иначе ничего

проблема scoped аллокаторов - если мы заводим контейнер, состоящий из других контейнеров и хотим, чтобы всё выделялось нестандартным аллокатором - то будет столько же объектов нестандартного аллокатора, сколько объектов контейнеров в контейнере (напрмер вектор строк - вектор при добавлении строк будет создавать каждой аллокатор по умолчанию - нестандартный аллокатор, но мы бы хотели, чтобы аллокатор был один и тот же как для самого вектора, так и для объектов в нём)

для решения этой проблемы в стандартной библиотеке есть класс scoped_allocator_adaptor (С++11)

std::vector<MyString, std::scoped_allocator_adaptor<MyAlloc<MyString>> v(alloc); теперь вектор будет создавать все строки одним аллокатором также туда можно передать разные аллокаторы каждый на разный уровень

устроен он так: в методе construct он должен передать в конструкторы T экземпляр аллокатора

упрощенная реализация:

std::uses_allocator - метафункция, возвращающая если в типе есть внутренний тип с allocator_type и он таков, что его можно создать из нашего аллокатора