0

亲爱的,这将很难:我创建了一个游戏对象工厂,可以生成我想要的对象。但是,我遇到了无法修复的内存泄漏。

内存泄漏是由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 矢量容器中。

我之所以选择这种方式,是因为我解析了一个对象映射文件。我有对象类型 ID(整数值),我需要将它们转换为真实对象。因为我有超过 100 种不同的对象数据类型,所以我希望避免连续遍历很长的 Switch() 语句。

因此,为了创建一个对象,我通过 CGameObjectFactory::create() 调用vFactories'['nEnumObjectTypeID']'( ) 来调用生成所需对象的存储函数。

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
4

7 回答 7

3

所以问题仍然存在,如何进行垃圾收集并避免报告的内存泄漏?

考虑使用std::shared_ptrboost::shared_ptr来管理您的BaseObject指针所有权。

于 2011-03-04T03:00:12.367 回答
2

这一行:

delete vFactories[nLoop]();

调用 new,然后立即删除该对象。它不会删除工厂创建的其他对象。您的泄漏检测工具是否为您提供未删除分配的堆栈跟踪?如果没有,那就买一个。

于 2011-03-04T03:07:55.160 回答
1

ReleaseClassTypes方法有缺陷:

delete vFactories[nLoop]();

基本上是在说:

delete new Object();

您正在删除刚刚创建的对象,而不是通过调用创建的所有对象CGameObjectFactory::create()。也就是说,您需要另一个向量来存储所有创建的对象,以便您可以一次将它们全部转储。

于 2011-03-04T03:00:29.587 回答
1

您可以从使用 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

看看是否有帮助。

于 2011-03-04T03:01:02.317 回答
1

正如其他人所说,研究shared_ptr.

但是如果你真的想做你认为你的代码正在做的事情,你的 create 和 release 方法应该看起来更像这样(你还需要 avector来存储 created 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; }
于 2011-03-04T06:01:47.050 回答
0

建议的解决方案 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
于 2011-03-04T17:00:20.107 回答
0

建议的解决方案 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

代码编译得很好。但是,我收到错误消息:

调试错误!

检测到堆损坏:在 0x04DC7E68 的正常块 (#949) 之后。

CRT 检测到应用程序在堆缓冲区结束后写入内存。

内存分配在:CGameObjectFactory 中行的文件路径:return BaseObjectPtr(new Object());*

I have three options in the Popup window: Abort, Retry, Ignore

If I repeadetly press Ignore, the vector container of allocated game pointers gets apparently deleted, because, I'll end up with NO MEMORY LEAKS. On the other hand, if I press Abort, I end up with my memory leaks again.

Any idea what that might indicate? I gues, I haven't done any specialties in my code, so I completely do not understand this behavior.

于 2011-03-04T18:51:46.970 回答