Пользовательский объем памяти и dealloc, который несколько классов Inheritance

Я хочу сделать управление памятью в моем проекте. я не хочу, чтобы оператор global new/delete, поэтому я реализую простой алгоритм памяти. это мой код:

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 не равен. поэтому память не будет корректной, и информация трассировки выделения не будет удалять coorect тоже... oh...

Есть ли способ решить эту проблему?

Ответ 1

Если ptr указывает на экземпляр полиморфного класса, dynamic_cast<void*>(ptr) приведет к указателю на самый производный объект, на который указывает ptr. Другими словами, этот динамический бросок дает указатель на адрес, выделенный как адрес.

Однако использование g_Allocator->Dealloc(dynamic_cast<void*>(ptr)) не является жизнеспособным решением. Проблема в том, что dynamic_cast<void*>(ptr) является незаконным, если ptr указывает на объект неклассов (например, примитив) или экземпляр неполиморфного класса.

Что вы можете сделать, это использовать SFINAE для создания функции, которая использует этот динамический приведение для указателей на полиморфные классы, но использует статическое приведение для указателей к объектам неклассов и экземплярам неполиморфных классов. Boost (и теперь С++ 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);
}

Ответ 2

Вы можете попробовать переопределить операторы 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

В этом случае оператор удаляет полученный правильный адрес.