Как и обещал в прошлой заметке - напишу как сделать аллокатор для stl контейнеров с поддержкой тегов.
Заходим в любую документацию по C++, лучше всего в стандарт, конечно, но можно куда-нибудь, где по нагляднее, например сюда, и реализуем всё что нужно для стандартного аллокатора. Пишем.
namespace bm {
// в параметры шаблона добавляем тип тега
template < typename T, E_HEAP_TAGS TAG >
class StlAllocator {
// адаптация для const типов
template < typename U >
struct AdaptT { typedef U type; };
template < typename U >
struct AdaptT<const U> { typedef U type; };
public:
// типы требующиеся для контейнеров и пользовательских контейнеров
typedef typename AdaptT<t>::type value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef const value_type* const_pointer;
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
// функции получения указателя из ссылки
// опять же требуется стандартом
pointer address(reference x) const { return &x; }
const_pointer address(const_reference x) const { return &x; }
// 1*
template < typename U >
struct rebind { typedef StlAllocator<U,TAG> other; };
// Конструкторы по-умолчанию, копирования,
// копирования из другого типа, но того же тега и деструктор.
StlAllocator() {}
StlAllocator(const StlAllocator&) {}
template < typename U >
StlAllocator(const StlAllocator<U,TAG>&) {}
~StlAllocator() {}
// собственно функция выделения памяти.
// используем наш перегруженный оператора new с тегом
pointer allocate(size_type count, const void* = NULL) {
void* memory = ::operator new(count * sizeof(T), TAG);
return static_cast<pointer>(memory);
}
// функция освобождения памяти.
// используем наш перегруженный оператор delete с тегом
void deallocate(pointer ptr, size_type) {
::operator delete(ptr, TAG);
}
// конструирование объекта в памяти.
// используем обычный placement new
void construct(pointer ptr, const T& val) {
::new(ptr) value_type(val);
}
// уничтожение объекта из памяти.
// зовем вручную деструктор
void destroy(pointer ptr) {
ptr->~value_type();
}
// максимальное количество элементов, которое может выделить аллокатор.
size_type max_size() const {
size_type count = size_type(-1) / sizeof(T);
return count > 0 ? count : 1;
}
};
// внешние операторы сравнения аллокаторов
template < typename T1, typename T2, E_HEAP_TAGS TAG >
inline bool operator == (const StlAllocator<T1,TAG>&, const StlAllocator<T2,TAG>&) {
return true;
}
template < typename T, E_HEAP_TAGS TAG, typename OtherAllocator >
inline bool operator == (const StlAllocator<T,TAG>&, const OtherAllocator&) {
return false;
}
template < typename T1, typename T2, E_HEAP_TAGS TAG >
inline bool operator != (const StlAllocator<T1,TAG>&, const StlAllocator<T2,TAG>&) {
return false;
}
template < typename T, E_HEAP_TAGS TAG, typename OtherAllocator >
inline bool operator != (const StlAllocator<T,TAG>&, const OtherAllocator&) {
return true;
}
} // namespace bm
1*) Шаблон для ребинда текущего типа аллокатора на другой тип. Используется в “node-base” контейнерах, аля std::map
, std::list
и т.д. Нужен потому что в этих контейнерах память выделяется не под пользовательский тип, а под ноду, где хранится этот тип. Что-то типа: struct Node { Node* prev; Node* next; T user_type; };
Добавлю еще немного о том, почему тэг передаётся в виде параметра шаблона, а не в конструкторе и не хранится в самом объекте аллокатора. Так исторически сложилось, что стандарт только рекомендует учитывать, что аллокатор может иметь стейт. (в С++11 это исправили как могли, но до многих новые плюсЫ еще не дошли), поэтому использование стейтов в аллокаторе практически невозможно, ибо на разных реализациях они будут по-разному себя вести. Передавая тег в качестве параметра шаблона, мы обходим эту проблему, но так же лишаемся возможности копировать, присваивать, обменивать и т.д. контейнеры с разными типами тегов.
Проверяем что получилось:
void stl_test() {
typedef std::vector<int, StlAllocator<int, HEAP_STL_CONTAINERS_TAG> > TVec;
TVec vec(10, 5);
vec.reserve(30);
typedef std::list<int, StlAllocator<int, HEAP_STL_CONTAINERS_TAG> > TList;
TList lst;
lst.push_back(1);
}
Отлично, то что и было нужно. Теперь можно повешать хук на всё это и посмотреть что и куда у вас аллочит :)