Я хочу сделать управление памятью в моем проекте. Я не хочу оператора глобального нового / удаления, поэтому я реализую простой аллокатор памяти. это мой код:
class IAllocator
{
public:
void* Alloc( unsigned int size )
{
1. alloc memory.
2. trace alloc.
}
void Dealloc( void* ptr )
{
1. free memory.
2. erase trace info.
}
template< typename T >
void Destructor( T* ptr )
{
if ( ptr )
ptr->~T();
}
};
// macro for use easy.
# define MYNEW( T ) new ( g_Allocator->Alloc( sizeof( T ) ) ) T
# define MYDEL( ptr ) if (ptr) { g_Allocator->Destructor(ptr); g_Allocator->Dealloc(ptr); }
Затем я могу использовать MYNEW для создания объекта (также трассировать информацию о выделении для проверки утечки памяти) и MYDEL для уничтожения объекта (удалить информацию о трассировке).
Все выглядит хорошо … но, когда я пытаюсь использовать этот метод для множественного класса наследования, я обнаружил очень серьезную проблему. посмотрите мой тестовый код ниже:
class A { ... };
class B { ... };
class C : public A, public B { ... };
C* pkC = MYNEW( C );
B* pkB = (B*)pkA;
MYDEL( pkB );
адреса pkB и pkA не равны. так что память не будет освобождена правильно, и информация трассировки alloc не сотрет слишком корректно … ох …
Есть ли способ решить эту проблему?
Если ptr
указывает на экземпляр полиморфного класса, dynamic_cast<void*>(ptr)
приведет к указателю на наиболее производный объект, на который указывает ptr
, Другими словами, это динамическое приведение дает указатель на назначенный адрес.
Однако, используя g_Allocator->Dealloc(dynamic_cast<void*>(ptr))
не является жизнеспособным решением. Проблема в том, что dynamic_cast<void*>(ptr)
незаконно, если ptr
указывает на неклассный объект (например, примитив) или на экземпляр неполиморфного класса.
То, что вы можете сделать, это использовать SFINAE для создания функции, которая использует это динамическое приведение для указателей на полиморфные классы, но использует статическое приведение для указателей на неклассовые объекты и экземпляры неполиморфных классов. Boost (и теперь C ++ 11) обеспечивает is_class<T>
а также is_polymorphic<T>
наберите черты, которые помогут в этом отношении.
Пример:
template <typename T, bool is_poly>
struct GetAllocatedPointerHelper {
static void* cast (T* ptr) { return ptr; }
};
template <typename T>
struct GetAllocatedPointerHelper<T, true> {
static void* cast (T* ptr) { return dynamic_cast<void*>(ptr); }
};
template<typename T>
inline void*
get_allocated_pointer (T* ptr)
{
const bool is_poly = Boost::is_polymorphic<T>::value;
return GetAllocatedPointerHelper<T, is_poly>::cast(ptr);
}
Вы можете попытаться переопределить операторы new и delete для базового класса и получить все ваши классы, где вы хотите, чтобы ваши пользовательские распределители были из этого класса. После простого примера:
#include <cstdio>
#include <cstdlib>
class Base
{
public:
virtual ~Base(){};
void* operator new(size_t size){return malloc(size);}
void operator delete(void* pointer){printf("\n%x\n", pointer); free(pointer);}
};
class A : public virtual Base
{
public:
~A(){printf("~A");};
};
class B : public virtual Base
{
public:
~B(){printf("~B");};
};
class C : public A, public B
{
public:
~C(){printf("~C");};
};
int main()
{
C* c = new C();
printf("%x\n", c);
B* b = dynamic_cast<B*>(c);
printf("%x\n", b);
delete b;
return 0;
}
Один из возможных выводов:
5831d0 5831d4 ~ C ~ B ~ A 5831d0
В этом случае оператор удалит полученный правильный адрес.