Шаг 558.
Библиотека STL.
Распределители памяти. Пользовательский распределитель

    На этом шаге мы рассмотрим пример создания пользовательского распределителя.

    Написать собственный распределитель не так уж сложно. Самый важный вопрос - как будет организовано выделение или освобождение памяти? Все остальное более или менее очевидно. Для примера рассмотрим упрощенную реализацию распределителя по умолчанию:

namespace std {
   template <class T>
   class allocator {
     public:
       // Определения типов
       typedef size_t    size_type;
       typedef ptrdiff_t difference_type;
       typedef T*        pointer;
       typedef const T*  const_pointer;
       typedef T&        reference;
       typedef const T&  const_reference;
       typedef T         value_type;

       // Привязка распределителя к типу U
       template <class U>
       struct rebind {
           typedef allocator<U> other;
       };

       // Функции возвращают адреса значений
       pointer address (reference value) const {
           return &value;
       }
       const_pointer address (const_reference value) const {
           return &value;
       }

       /* Конструкторы и деструктор
        * - ничего не делаем, поскольку распределитель не имеет состояния
        */
       allocator() throw() {
       }
       allocator(const allocator&) throw() {
       }
       template <class U>
         allocator (const allocator<U>&) throw() {
       }
       ~allocator() throw() {
       }

       // Функция возвращает максимальное количество элементов,
       // для которых может быть выделена память
       size_type max_size () const throw() {
           return numeric_limits<size_t>::max() / sizeof(T);
       }

       // Выделение памяти для num элементов типа T без инициализации
       pointer allocate (size_type num,
                         allocator<void>::const_pointer hint = 0) {
           // allocate memory with global new
           return (pointer)(::operator new(num*sizeof(T)));
       }

       // Инициализация элементов выделенного блока p значением value
       void construct (pointer p, const T& value) {
           // Инициализация памяти оператором new
           new((void*)p)T(value);
       }

       // Удаление элементов инициализированного блока p
       void destroy (pointer p) {
           // Уничтожение объектов вызовом деструктора
           p->~T();
       }

       // Освобождение блока p, содержащего удаленные элементы
       void deallocate (pointer p, size_type num) {
           // Освобождение памяти глобальным оператором delete
           ::operator delete((void*)p);
       }
   };
   // Оператор сообщает, что все специализации данного распределителя
   // являются взаимозаменяемыми.
   template <class T1, class T2>
   bool operator== (const allocator<T1>&,
                    const allocator<T2>&) throw() {
       return true;
   }
   template <class T1, class T2>
   bool operator!= (const allocator<T1>&,
                    const allocator<T2>&) throw() {
       return false;
   }
}

    Используя эту базовую реализацию, вы можете без особых трудностей реализовать собственный распределитель. Как правило, все отличия от приведенной реализации сосредоточены в функциях max_size(), allocate() и deallocate(). В этих трех функциях вы программируете политику выделения памяти (например, повторного использования памяти вместо се немедленного освобождения), расходования общей памяти или отображения памяти на сегменты объектно-ориентированной базы данных.

    На следующем шаге мы рассмотрим требования к распределителям памяти.




Предыдущий шаг Содержание Следующий шаг