c ++ Сборка мусора и вызов деструкторов - PullRequest
2 голосов
/ 08 января 2010

В каждом кадре мне нужно выделить некоторые данные, которые должны остаться до конца кадра.

В настоящее время я выделяю данные из другого пула памяти, что позволяет пометить их с помощью счетчика кадров. В конце кадра я обхожу пул памяти и удаляю память, выделенную в конкретном кадре.

Проблема, с которой я сталкиваюсь, заключается в том, что для удержания данных я должен поместить их в структуру таким образом:

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 деструктор НИКОГДА не вызывается для объекта.

Я пытался найти способы использования шаблонных объектов со смарт-указателем для решения, но для этого мне пришлось перегрузить шаблонный класс в базовый тип без шаблонов, что делает удаление еще более трудным .

У кого-нибудь есть решение такой проблемы?

Ответы [ 6 ]

6 голосов
/ 08 января 2010

Если вы не хотите принудительно наследовать все объекты от Destructible, вы можете сохранить указатель на функцию удаления (или функтор) вместе с указателем на сами данные.Клиентский код отвечает за предоставление функции, которая знает, как правильно удалять данные, обычно что-то вроде:

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

Хотя удалитель будет обычно во многом как выше, этотакже дает клиенту возможность сохранять сложные структуры данных и по-прежнему правильно их удалять.

6 голосов
/ 08 января 2010
class Destructable
{
public:
   virtual ~Destructable() {}
};

Вместо void *, храните Destructable * в пуле. Сделайте объекты, выделенные из пула, наследуемыми от Destructable.

Либо переопределите операторы new и delete для соответствующих классов. Заставьте их использовать бассейн. Удалите объект, как только ЦПУ закончит работу с ним, в коде, который владеет им и, следовательно, знает его правильный тип; поскольку пул не будет повторно использовать память до тех пор, пока не увидит соответствующий конец кадра, любое асинхронное оборудование потребовало, чтобы вы задержали сборку мусора таким образом, все еще может делать свое дело.

0 голосов
/ 08 января 2010

Я бы сделал что-то вроде этого:

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 на виртуальный деструктор,Я не уверен, что это то, что вы ищете, так как я не понимаю, что «я пытался найти способы использования шаблонных объектов со смарт-указателем для решения, но для этого мне пришлось перегружатьшаблонный класс для базового типа без шаблонов, что делает удаление еще более трудным ".значит, но я надеюсь, что это полезно.

Это требует, чтобы код управления памятью каким-то образом имел доступ к объявлениям того, что вы хотите освободить, но я не думаю, что есть какой-то способ обойти это, если вы хотитевызываемые деструкторы, что вы явно делаете.

0 голосов
/ 08 января 2010

Первое, о чем я могу подумать, это использовать 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;
};

Если вы хотите реализовать нечто подобное вручную, вы можете использовать указатель функции «delete» для обработки удаления объектов вместо непосредственного вызова 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(), который был выполнен в деструкторе, но это заняло бы больше времени, чем у меня сейчас, чтобы сделать правильно (то есть решить, как будут обрабатываться копии и так далее ...

0 голосов
/ 08 января 2010

Если вы имеете в виду фрейм стека (т.е. внутри функции) вы можете попробовать использовать alloca ()

0 голосов
/ 08 января 2010

Единственный способ сделать это - добавить запись типа в структуру FrameMemory, а затем использовать ее для правильного приведения памяти для удаления. Например, если у вас есть память типа foo, вы можете получить что-то вроде:

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

    delete theMemory;
}

Обратите внимание, что это может быть ***** ***** опасная операция, если вы делаете это неправильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...