c ++垃圾收集和调用析构函数

时间:2010-01-08 15:29:08

标签: c++ garbage-collection

每帧我需要分配一些需要保留的数据,直到帧结束。

目前,我正在从不同的内存池中分配数据,这允许我用帧计数标记它。在帧结束时,我遍历内存池并删除在特定帧中分配的内存。

我遇到的问题是,为了保持数据,我必须将它放在一个结构中:

struct FrameMemory
{
    uint32 frameIndex;
    bool allocatedType; //0 = new(), 1 = new[]
    void* pMemPtr;
}

所以后来,当我开始释放内存时,它看起来像这样:

{
for(all blocks)
 if(block[i].frameIndex == targetIndex)
     if(block[i].allocatedType == 0)
          delete block[i].pMemPtr;
     else if (block[i].allocatedType ==1)
          delete[] block[i].pMemPtr;
}

问题在于,因为我必须将指向内存的指针重载为void *,所以DELETE运算符不能正确地将内存作为其“本机基类型”删除。 IE析构函数NEVER为对象调用。

我试图找到使用智能指针模板化对象的方法,但为了做到这一点,我必须将模板化的类重载为非模板化的基类型,这使得删除更加困难

有没有人能解决这样的问题?

6 个答案:

答案 0 :(得分:6)

class Destructable
{
public:
   virtual ~Destructable() {}
};

而不是void *,将Destructable *存储在池中。使从池中分配的对象继承自Destructable。

或者,覆盖相关类的运算符newdelete。让他们使用游泳池。 CPU完成后,以常规方式,在拥有它的代码中删除对象,因此知道它的正确类型;因为池在看到适当的帧结束之前不会重用内存,所以无论你以这种方式延迟垃圾收集所需的异步硬件仍然可以做到这一点。

答案 1 :(得分:6)

如果您不想强制所有对象继承自Destructible,您可以存储指向删除函数(或仿函数)的指针以及指向数据本身的指针。客户端代码负责提供一个知道如何正确删除数据的函数,通常类似于:

void xxx_deleter(void *data) { 
    xxx *ptr = static_cast<xxx *>(data);
    delete ptr;
}

虽然删除器通常与上面的很相似,但这也为客户提供了存储复杂数据结构并仍然可以正确删除它们的选项。

答案 2 :(得分:0)

我能想到的唯一方法是在FrameMemory结构中添加一个类型条目,然后使用它来正确地为删除内存。例如,如果你有foo类型的内存,你可以有类似的东西:

if (block[i].BlockType == BLOCKTYPE_FOO)
{
    foo *theMemory = (foo *)block[i].pMemPtr;

    delete theMemory;
}

请注意,如果你做错了,这可能是一个非常危险的****操作。

答案 3 :(得分:0)

如果你是平均堆栈帧(即内部函数) 你可以尝试使用alloca()

答案 4 :(得分:0)

我能想到的第一件事是使用boost::shared_ptr<void>(对于非数组版本,可能需要一些工作来使其适应数组版本)作为指针类型。而且我认为应该照顾大部分细节。每当帧被破坏时,内存将被适当删除:

struct FrameMemory
{
    uint32 frameIndex;
//    bool allocatedType; //0 = new(), 1 = new[] only works with instances, not arrays
    boost::shared_ptr<void> pMemPtr;
};

如果要手动实现类似的东西,可以使用'deleter'函数指针来处理对象的删除,而不是直接调用delete。以下是修改代码的粗略方法:

// helper deleter functions
template <typename T>
void object_deleter( void *p ) {
   delete static_cast<T*>(p);
}
template <typename T>
void array_deleter( void *p ) {
   delete [] static_cast<T*>(p);
}

class FrameMemory
{
public:
    const uint32 frameIndex;
    void* pMemPtr;
private:
    void (*deleter)(void*);
public:
    template <typename T>
    FrameMemory( uint32 frame, T* memory, bool isarray = false )
       : frameIndex(frame), pMemPtr(memory), 
         deleter( isarray? array_deleter<T> : object_deleter<T> )
    {}
    void delete() {
       deleter(pMemPtr)
    }
};
struct X;
void usage()
{
    {
       FrameMemory f( 1, new X );
       f.delete();
    }
    {
       FrameMemory f( 1, new x[10], true );
       f.delete();
    }
}

我会进一步修改它,以便不必调用在析构函数中执行的FrameMemory::delete(),但这需要比我现在花费更多时间才能正确执行(即,决定副本是如何进行的处理等......

答案 5 :(得分:0)

我会做这样的事情:

struct FrameMemoryBase
{
    uint32 frameIndex;
    bool allocatedType; //0 = new(), 1 = new[]
    virtual void Free() = 0;
};

template <typename T>
struct FrameMemory : public FrameMemoryBase
{
  void Free()
  {
     if(allocatedType == 0)
         delete pMemPtr;
     else if (allocatedType ==1)
         delete[] pMemPtr;
  }

  T *pMemPtr;
};

您将通过以下方式使用:

{
for(all blocks)
    if(block[i].frameIndex == targetIndex)
        block[i].Free(); 
}    

如果您还释放了FrameMemory结构,您可以将Free更改为虚拟析构函数。我不确定这是你在找什么,因为我不明白“我试图找到使用智能指针模板化对象的方法,但为了做到这一点,我必须超载模板化的类到非模板化的基类型,这使得删除更加困难。“意思是,但我希望这是有帮助的。

这要求内存管理代码以某种方式访问​​您希望释放的声明,但我认为没有任何解决方法,假设您需要调用析构函数,您明确地这样做。