Раз уж начал говорить про память, покажу простой способ навешать теги на аллокации с возможность хука на них, с помощью которого можно сделать чудо вещи, например, трекинг памяти.
Для начала нам нужны простые функции выделения памяти, которые мы будем использовать вместо системных. Так же понадобятся енумы для тегов аллокаций и действий хипа.
Где-то в хидере в самом низу вашего проекта:
namespace bm {
// теги для аллокаций
enum E_HEAP_TAGS {
HEAP_BM_SYSTEM_TAG,
HEAP_STL_STRINGS_TAG,
HEAP_STL_CONTAINERS_TAG,
HEAP_UNKNOWN_TAG
};
// енум для действий хипа
enum E_HEAP_ACTS {
HEAP_ACT_ALLOC,
HEAP_ACT_FREE
};
// тип функции для хука
typedef void (*THeapHook)(
E_HEAP_ACTS act, E_HEAP_TAGS tag,
void* ptr, size_t size);
// функции выделения и очищения памяти
void* Heap_Alloc ( size_t size, E_HEAP_TAGS tag );
void Heap_Free ( void* ptr, E_HEAP_TAGS tag );
// функция установки пользовательского хука
THeapHook Heap_SetHook ( THeapHook hook );
}
Реализуем всё то, что объявили выше. Где-то в исходниках:
// макросы для системных функций выделения и очистки памяти
#define BM_Sys_Alloc ::malloc
#define BM_Sys_Free ::free
namespace bm {
namespace allocs_detail {
// глобальный текущий хук, по-умолчанию пустой
static THeapHook heap_hook = NULL;
// функция вызова текущего хука, будет зваться из наших Heap_Alloc и Heap_Free
static void CallHeapHook(
E_HEAP_ACTS act, // действие
E_HEAP_TAGS tag, // тег
void* ptr, // память которую выделяем\удаляем
size_t size) // размер куска памяти
{
if ( heap_hook )
heap_hook(act, tag, ptr, size);
}
}
// реализация нашего выделения памяти
void* Heap_Alloc(size_t size, E_HEAP_TAGS tag) {
// выделяем память с помощью системной функции и зовём хук
void* ptr = BM_Sys_Alloc(size);
allocs_detail::CallHeapHook(HEAP_ACT_ALLOC, tag, ptr, size);
return ptr;
}
// реализация нашего освобождения памяти
void Heap_Free(void* ptr, E_HEAP_TAGS tag) {
// зовем сначало хук, пока память жива, потом удаляем системной функцией
allocs_detail::CallHeapHook(HEAP_ACT_FREE, tag, ptr, 0);
BM_Sys_Free(ptr);
}
// реализация установки пользовательского хука
THeapHook Heap_SetHook( THeapHook hook ) {
// устанавливаем новый и возвращаем старый (вдруг кому понадобится)
using allocs_detail::heap_hook;
THeapHook last = heap_hook;
heap_hook = hook;
return last;
}
}
С функциями закончили. Теперь вместо обычных malloc
и free
нужно звать Heap_Alloc
и Heap_Free
с нужными тегами, не забывая про “3rd party”-библиотеки, которые в большинстве своем предлагают определять для них функции выделения памяти, если же нет, то придется ручками менять, а лучше просто гнать такие библиотеки в шею, ибо не дело это :)
Ок, с malloc
и free
всё понятно, что же делать с new
и delete
? Всё просто - переопределяем их на свои:
inline void* operator new(size_t size, bm::E_HEAP_TAGS tag)
{ return bm::Heap_Alloc(size, tag); }
inline void* operator new[](size_t size, bm::E_HEAP_TAGS tag)
{ return bm::Heap_Alloc(size, tag); }
inline void* operator new(size_t size)
{ return bm::Heap_Alloc(size, bm::HEAP_UNKNOWN_TAG); }
inline void* operator new[](size_t size)
{ return bm::Heap_Alloc(size, bm::HEAP_UNKNOWN_TAG); }
inline void operator delete(void* ptr, bm::E_HEAP_TAGS tag)
{ bm::Heap_Free(ptr, tag); }
inline void operator delete[](void* ptr, bm::E_HEAP_TAGS tag)
{ bm::Heap_Free(ptr, tag); }
inline void operator delete(void* ptr)
{ bm::Heap_Free(ptr, bm::HEAP_UNKNOWN_TAG); }
inline void operator delete[](void* ptr)
{ bm::Heap_Free(ptr, bm::HEAP_UNKNOWN_TAG); }
Теперь:
new TestClass()
- будет выделять память через наши функции с HEAP_UNKNOWN_TAG
тегом.
new (HEAP_BM_SYSTEM_TAG) TestClass()
- с тегом заданным в параметре new
.
Так же, по вкусу, можно переопределить nothrow new
, если сие нужно для вашего проекта.
И с этим разобрались. Понятное дело, что вызывать new
таким образом неудобно, по-этому можно написать немного помогающего кода:
namespace bm {
template < E_HEAP_TAGS TAG >
class Allocable {
public:
void* operator new (size_t size) { return ::operator new (size, TAG); }
void* operator new[] (size_t size) { return ::operator new[] (size, TAG); }
void operator delete (void* ptr) { ::operator delete ( ptr, TAG); }
void operator delete[](void* ptr) { ::operator delete[]( ptr, TAG); }
};
}
Простой классик, наследуясь от которого можно получить нужный тег по-умолчанию, например:
class Texture : public Allocable<E_HEAP_TEXTURES_TAG>
{...};
// память будет выделена с тегом E_HEAP_TEXTURES_TAG
Texture* texture = new Texture();
В следующей части расскажу как это всё прикрутить к STL контейнерам, ибо их-то аллокации тоже чекать надо.