Фабрика игровых объектов: устранение утечек памяти - PullRequest
0 голосов
/ 04 марта 2011

Дорогие все, это будет сложно: я создал фабрику игровых объектов, которая генерирует объекты моего желания.Тем не менее, я получаю утечки памяти, которые я не могу исправить.

Утечки памяти генерируются return new Object (); в нижней части образца кода.

static BaseObject * CreateObjectFunc()
{
    return new Object();
}

Как и где удалить указатели?Я написал bool ReleaseClassType () .Несмотря на то, что фабрика работает хорошо, ReleaseClassType () не устраняет утечки памяти.

bool ReleaseClassTypes()
{
    unsigned int nRecordCount = vFactories.size();
    for (unsigned int nLoop = 0; nLoop < nRecordCount; nLoop++ )
    {
        // if the object exists in the container and is valid, then render it
        if( vFactories[nLoop] != NULL) 
            delete vFactories[nLoop]();
    }
    return true;
}

Прежде чем взглянуть на приведенный ниже код, позвольте мне помочь вам в том, что мой CGameObjectFactory создает указатели на функции создание определенного типа объекта.Указатели хранятся в векторном контейнере vFactories.

Я выбрал этот способ, потому что я анализирую файл объектной карты.У меня есть идентификаторы типов объектов (целочисленные значения), которые мне нужны, чтобы перевести их в реальные объекты.Поскольку у меня более 100 различных типов данных объекта, я хотел бы избежать непрерывного обхода очень длинного оператора Switch ().

Поэтому, чтобы создать объект, я вызываю vFactories '[' nEnumObjectTypeID ']' () через CGameObjectFactory :: create () для вызова хранимой функции, которая генерирует нужный объект.

Положение соответствующей функции в vFactories идентично nObjectTypeID, поэтому я могу использовать индексирование для доступа к функции.

Таким образом, остается вопрос, , как поступить с мусоромсбор и избежать зарегистрированных утечек памяти?

#ifndef GAMEOBJECTFACTORY_H_UNIPIXELS
#define GAMEOBJECTFACTORY_H_UNIPIXELS

//#include "MemoryManager.h"
#include <vector>


template <typename BaseObject>
class CGameObjectFactory
{
public:
    // cleanup and release registered object data types
    bool ReleaseClassTypes()
    {
        unsigned int nRecordCount = vFactories.size();
        for (unsigned int nLoop = 0; nLoop < nRecordCount; nLoop++ )
        {
            // if the object exists in the container and is valid, then render it
            if( vFactories[nLoop] != NULL) 
                delete vFactories[nLoop]();
        }
        return true;
    }

    // register new object data type
    template <typename Object>
    bool RegisterClassType(unsigned int nObjectIDParam )
    {
        if(vFactories.size() < nObjectIDParam) vFactories.resize(nObjectIDParam);

        vFactories[nObjectIDParam] = &CreateObjectFunc<Object>;
        return true;
    }


    // create new object by calling the pointer to the appropriate type function
    BaseObject* create(unsigned int nObjectIDParam) const
    {
        return vFactories[nObjectIDParam]();
    }


    // resize the vector array containing pointers to function calls
    bool resize(unsigned int nSizeParam)
    {
        vFactories.resize(nSizeParam);
        return true;
    }

private:
    //DECLARE_HEAP;

    template <typename Object>
    static BaseObject * CreateObjectFunc()
    {
        return new Object();
    }


    typedef BaseObject*(*factory)();
    std::vector<factory> vFactories;
};


//DEFINE_HEAP_T(CGameObjectFactory, "Game Object Factory");

#endif // GAMEOBJECTFACTORY_H_UNIPIXELS

Ответы [ 7 ]

3 голосов
/ 04 марта 2011

Таким образом, остается вопрос, как продолжить сборку мусора и избежать зарегистрированных утечек памяти?

Попробуйте использовать std::shared_ptr или boost :: shared_ptr для управления BaseObject владением указателем.

2 голосов
/ 04 марта 2011

Эта строка:

delete vFactories[nLoop]();

Вызывает новый, а затем быстро удаляет объект. Он не удалит другие объекты, созданные фабрикой. Дает ли ваш инструмент обнаружения утечек трассировку стека выделения, которое не было удалено? Если нет, получите тот, который делает.

1 голос
/ 04 марта 2011

Как уже говорили другие, посмотрите на различные реализации shared_ptr.

Но если вы действительно хотите делать то, что, как вы думаете, делает ваш код, ваши методы создания и выпуска должны выглядеть примерно так (выВам также понадобится vector для хранения созданных BaseObject* s [называемых vObjects ниже], поскольку ваш текущий код хранит только фабрики, а не созданные объекты):

public:
BaseObject* create(unsigned int nObjectIDParam)
{
    BaseObject *obj = vFactories[nObjectIDParam]();
    //I'm assuming you have error handling/detection already in code that calls this create function

    vObjects.push_back(obj);
    return obj;
}

bool ReleaseClassTypes()
{
    for (typename vector<BaseObject*>::iterator iter = vObjects.begin(); iter != vObjects.end(); ++iter) {
        if (*iter) {
            delete *iter;
            *iter = NULL; //not strictly needed, but doesn't hurt
        }
    }
    vObjects.erase();
    return true; //you might as well just convert the return type to void
}

Но тогда вывероятно, следует кодировать деструктор для вызова ReleaseClassTypes:

public:
~CGameObjectFactory() {
    ReleaseClassTypes();
}

И с небольшим отклонением от Правило трех , вы, вероятно, захотите сделать конструктор копирования и оператор присваиванияконфиденциально, чтобы запретить копии (или вы могли бы правильно определить их, чтобы получить новые ресурсы и выпустить старые, но я не уверен, зачем вам нужно копировать фабрику).

private:
CGameObjectFactory(const CGameObjectFactory& cgoFact) { }
CGameObjectFactory& operator=(const CGameObjectFactory& cgoFact) { return *this; }
1 голос
/ 04 марта 2011

Вы можете начать с использования std :: shared_ptr или std :: tr1 :: shared_ptr или boost :: shared_ptr в зависимости от вашего компилятора.

Вы бы использовали это так:

typedef std::shared_ptr<BaseObject> BaseObjectPtr;
static BaseObjectPtr CreateObjectFunc()
{
    return BaseObjectPtr(new Object());
}

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

Итак, во втором примере кода:

#ifndef GAMEOBJECTFACTORY_H_UNIPIXELS
#define GAMEOBJECTFACTORY_H_UNIPIXELS

//#include "MemoryManager.h"
#include <vector>
#include <memory>

template <typename BaseObject>
class CGameObjectFactory
{
public:
    typedef std::shared_ptr<BaseObject> BaseObjectPtr;

    // cleanup and release registered object data types
    bool ReleaseClassTypes()
    {
        unsigned int nRecordCount = vFactories.size();
        for (unsigned int nLoop = 0; nLoop < nRecordCount; nLoop++ )
        {
            // if the object exists in the container and is valid, then render it
            //if( vFactories[nLoop] != NULL) 
            //    delete vFactories[nLoop]();
            // The above code would create something then immediately delete it.
            // You could keep a container of pointers to the objects you created
            // and loop through that instead, or use shared_ptr.
            // If you want to unregister the creator functions just NULL the pointer.
            vFactories[nLoop] = NULL;
        }
        return true;
    }

    // register new object data type
    template <typename Object>
    bool RegisterClassType(unsigned int nObjectIDParam )
    {
        if(vFactories.size() < nObjectIDParam) vFactories.resize(nObjectIDParam);

        // Store a pointer to the creation function
        vFactories[nObjectIDParam] = &CreateObjectFunc<Object>;
        return true;
    }


    // create new object by calling the pointer to the appropriate type function
    BaseObjectPtr create(unsigned int nObjectIDParam) const
    {
        return vFactories[nObjectIDParam]();
    }


    // resize the vector array containing pointers to function calls
    bool resize(unsigned int nSizeParam)
    {
        vFactories.resize(nSizeParam);
        return true;
    }

private:
    //DECLARE_HEAP;

    template <typename Object>
    static BaseObjectPtr CreateObjectFunc()
    {
        return BaseObjectPtr(new Object());
    }


    typedef BaseObjectPtr(*factory)();
    std::vector<factory> vFactories;
};


//DEFINE_HEAP_T(CGameObjectFactory, "Game Object Factory");

#endif // GAMEOBJECTFACTORY_H_UNIPIXELS

Посмотрите, поможет ли это.

1 голос
/ 04 марта 2011

Метод ReleaseClassTypes имеет недостатки:

delete vFactories[nLoop]();

в основном говорит:

delete new Object();

Вы удаляете только что созданный объект, а не все объекты, созданные с помощью вызова CGameObjectFactory::create().Тем не менее, вам понадобится еще один вектор для хранения всех созданных объектов, чтобы вы могли сбросить их все сразу.

0 голосов
/ 04 марта 2011

Предлагаемое решение № 2: Использование интеллектуальных указателей

- Еще не работает

Советы выше рекомендуют два решения. Второе решение рекомендует использовать умные указатели. Я заменил ключевые указатели на std :: smart_ptr, как показано ниже:

#ifndef GAMEOBJECTFACTORY_H_UNIPIXELS
#define GAMEOBJECTFACTORY_H_UNIPIXELS

#include "GameObject.h"
#include <vector>
#include <memory>


template <typename BaseObject>
class CGameObjectFactory
{
public:
    typedef std::shared_ptr<BaseObject> BaseObjectPtr;

    // cleanup and release registered object data types
    bool ReleaseClassTypes()
    {
        unsigned int nRecordCount = vFactories.size();
        for (unsigned int nLoop = 0; nLoop < nRecordCount; nLoop++ )
        {
            // if the object exists in the container, then delete it
            if( vFactories[nLoop] != NULL) vFactories[nLoop] = NULL;
        }
        return true;
    }


    // register new object data type
    template <typename Object>
    bool RegisterClassType(unsigned int nObjectIDParam )
    {
        if(vFactories.size() < nObjectIDParam) vFactories.resize(nObjectIDParam);
        vFactories[nObjectIDParam] = &CreateObjectFunc<Object>;

        return true;
    }


    // create new object by calling the pointer to the appropriate type function
    BaseObjectPtr create(unsigned int nObjectIDParam) const
    {
        return vFactories[nObjectIDParam]();        
    }


    // resize the vector array containing pointers to function calls
    bool resize(unsigned int nSizeParam)
    {
        vFactories.resize(nSizeParam);
        return true;
    }

private:
    //DECLARE_HEAP;

    template <typename Object>
    static BaseObjectPtr CreateObjectFunc()
    {   
        return BaseObjectPtr(new Object());
    }



    typedef  BaseObjectPtr(*factory)();
    std::vector<factory> vFactories;

};


//DEFINE_HEAP_T(CGameObjectFactory, "Game Object Factory");

#endif // GAMEOBJECTFACTORY_H_UNIPIXELS

Код скомпилирован нормально. Однако я получаю сообщение об ошибке:

Ошибка отладки!

ОБНАРУЖЕНА КОРРУПЦИЯ КАРТЫ: после нормального блок (# 949) в 0x04DC7E68.

CRT обнаружил, что приложение записал в память после завершения кучи буфер.

Память, выделенная в: filepath к строке в CGameObjectFactory: return BaseObjectPtr (new Object ()); *

У меня есть три варианта во всплывающем окне: Прервать , Повторить , Игнорировать

Если я повторно нажму Игнорировать , векторный контейнер выделенных игровых указателей, очевидно, будет удален, потому что, Я получу в итоге НЕТ УТЕЧКИ ПАМЯТИ . С другой стороны, если я нажму Прервать , у меня снова будут утечки памяти.

Есть идеи, что это может указывать? Я догадываюсь, я не специализировался в своем коде, поэтому я совершенно не понимаю этого поведения.

0 голосов
/ 04 марта 2011

Предлагаемое решение № 1: Использование внутреннего вектора для сгенерированных указателей

- Еще не работает

В приведенных выше рекомендациях рекомендуется два решения.Во-первых, это создать еще один внутренний вектор для записи всех указателей, сгенерированных: return new Object ();

template <typename Object>
static BaseObject* CreateObjectFunc()
{   
     return new Object();
}

Вектор следует затем удалить вручную, как в этой функции:

bool ReleaseClassTypes()
{
    unsigned int nRecordCount = vObjects.size();
    for (unsigned int nLoop = 0; nLoop < nRecordCount; nLoop++ )
    {
        if( vObjects[nLoop] != NULL) 
            delete vObjects[nLoop];
    }
    return true;
}

Я поступил согласно советам и устал от многих других комбинаций.Однако, Я получаю ошибки компиляции:

d:\source\satori\satori\gameobjectfactory.h(48): error C2663: 'std::vector<_Ty>::push_back' : 2 overloads have no legal conversion for 'this' pointer
1>          with
1>          [
1>              _Ty=CGameObject *
1>          ]
1>          d:\source\satori\satori\gameobjectfactory.h(45) : while compiling class template member function 'CGameObject *CGameObjectFactory<BaseObject>::create(unsigned int) const'
1>          with
1>          [
1>              BaseObject=CGameObject
1>          ]
1>          d:\source\satori\satori\resourcemanager.h(99) : see reference to class template instantiation 'CGameObjectFactory<BaseObject>' being compiled
1>          with
1>          [
1>              BaseObject=CGameObject
1>          ]

Вот модифицированный CGameObjectFactory, приводящий к ошибке компиляции. Любой хороший совет, где проблема сейчас, пожалуйста?

#ifndef GAMEOBJECTFACTORY_H_UNIPIXELS
#define GAMEOBJECTFACTORY_H_UNIPIXELS

#include "GameObject.h"
#include <vector>


template <typename BaseObject>
class CGameObjectFactory
{
public:
    //typedef std::shared_ptr<BaseObject> BaseObjectPtr;
    // cleanup and release registered object data types
    bool ReleaseClassTypes()
    {
        //unsigned int nRecordCount = vFactories.size();
        unsigned int nRecordCount = vObjects.size();
        for (unsigned int nLoop = 0; nLoop < nRecordCount; nLoop++ )
        {
            if( vObjects[nLoop] != NULL) 
                delete vObjects[nLoop];
        }
        return true;
    }


    // register new object data type
    template <typename Object>
    bool RegisterClassType(unsigned int nObjectIDParam )
    {
        if(vFactories.size() < nObjectIDParam) vFactories.resize(nObjectIDParam);
        vFactories[nObjectIDParam] = &CreateObjectFunc<Object>;

        return true;
    }


    // create new object by calling the pointer to the appropriate type function
    BaseObject* create(unsigned int nObjectIDParam) const
    {
        BaseObject* pObject = vFactories[nObjectIDParam]();
        vObjects.push_back(pObject);
        return pObject;
    }


    // resize the vector array containing pointers to function calls
    bool resize(unsigned int nSizeParam)
    {
        vFactories.resize(nSizeParam);
        return true;
    }

private:
    //DECLARE_HEAP;

    template <typename Object>
    static BaseObject* CreateObjectFunc()
    {   
        return new Object();
    }



    typedef  BaseObject* (*factory)();
    std::vector<factory> vFactories;
    std::vector<BaseObject*> vObjects;

};


//DEFINE_HEAP_T(CGameObjectFactory, "Game Object Factory");

#endif // GAMEOBJECTFACTORY_H_UNIPIXELS
...