11

我想在我的项目中进行内存管理。我不想要操作员全局新/删除,所以我实现了一个简单的内存分配器。这是我的代码:

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 ......哦......

有什么办法可以解决这个问题吗?</p>

4

2 回答 2

7

如果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);
}
于 2012-12-04T15:13:27.597 回答
4

您可以尝试覆盖基类的运算符 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

在这种情况下,操作员删除收到的正确地址。

于 2012-12-04T15:07:21.547 回答