0

Dear all, this is going to be tough: I have created a game object factory that generates objects of my wish. However, I get memory leaks which I can not fix.

Memory leaks are generated by return new Object(); in the bottom part of the code sample.

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

How and where to delete the pointers? I wrote bool ReleaseClassType(). Despite the factory works well, ReleaseClassType() does not fix memory leaks.

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;
}

Before taking a look at the code below, let me help you in that my CGameObjectFactory creates pointers to functions creating particular object type. The pointers are stored within vFactories vector container.

I have chosen this way because I parse an object map file. I have object type IDs (integer values) which I need to translate them into real objects. Because I have over 100 different object data types, I wished to avoid continuously traversing very long Switch() statement.

Therefore, to create an object, I call vFactories'['nEnumObjectTypeID']'() via CGameObjectFactory::create() to call stored function that generates desired object.

The position of the appropriate function in the vFactories is identical to the nObjectTypeID, so I can use indexing to access the function.

So the question remains, how to proceed with garbage collection and avoid reported memory leaks?

#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
Bunkai.Satori
  • 4,698
  • 13
  • 49
  • 77
  • Are these factories supposed to allow multiple instances of the class they create or only allow singletons? – user470379 Mar 04 '11 at 02:57
  • Hi, thanks for your interest. the factories are supposed to create **infinitely many of new objects** of particular type. Somewhere in my code I call CGameObjectFactory::create(nObjectTypeID); I will receive a pointer to newly created object of nObjectTypeID. I store those pointers in another vector called vGameMap, and of course I delete them before application exist, despite, as I know, vector deletes pointers on its own. – Bunkai.Satori Mar 04 '11 at 03:06

7 Answers7

3

So the question remains, how to proceed with garbage collection and avoid reported memory leaks?

Consider using std::shared_ptr or boost::shared_ptr to manage your BaseObject pointer ownership.

Sam Miller
  • 23,808
  • 4
  • 67
  • 87
  • +1 and thanks for the effor to take a look at my long question. You are correct, I haven't considered shared pointers yet. I will consider them, and it is very good idea. – Bunkai.Satori Mar 04 '11 at 03:12
2

This line:

delete vFactories[nLoop]();

Calls new, and then promptly deletes the object. It won't delete other objects that have been created by the factory. Does your leak detection tool give you stack trace of the allocation that wasn't deleted? If not, get one that does.

Lou Franco
  • 87,846
  • 14
  • 132
  • 192
  • +1 for very good point. Thanks for your effort invested into my question. Basically, I have very simple method of tracking of memory leaks: I use Visual Studio 2010 **NEW_DEBUG** overloaded operand. Would you be so kind to advice me some better leak detection tool? would you have advice how to fix my issue? – Bunkai.Satori Mar 04 '11 at 03:20
1

The ReleaseClassTypes method is flawed:

delete vFactories[nLoop]();

is basically saying:

delete new Object();

You are deleting the object you just created, not all the objects created by calling CGameObjectFactory::create(). That said, you'll need another vector to store all the created objects so you can dump them all at once.

arul
  • 13,998
  • 1
  • 57
  • 77
  • +1 for effort to understand my question and very good help. Hi, arul, is it clear to you that every object I create with **CGameObjectFactory::create()** is stored in another array, which I of course delete. Could I kindly ask for some more quidance, what else should I keep in another vector? – Bunkai.Satori Mar 04 '11 at 03:11
  • `ReleaseClassTypes` doesn't fix anything, because basically it's a no-op. Delete the objects when you no longer need them, or as others have suggested, use smart pointers. – arul Mar 04 '11 at 03:19
  • thanks for your navigation. I have HEAP CORRUPTION error DETECTED. It is related to the memory allocated by the line: **return new Object();** in the code above. I decided to solve problem one by one, and for the time being, I was overlooking Debug Error! window, as I did not think it would relate to reported memory leaks. – Bunkai.Satori Mar 04 '11 at 20:04
1

You can start by using std::shared_ptr or std::tr1::shared_ptr or boost::shared_ptr depending on your compiler.

You would use it like this:

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

You won't need to release the created resources. They will do automatic reference counting and deallocate themselves when there are no strong references pointing to it.

So in your second code example:

#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

See if that helps.

Julien Lebot
  • 3,092
  • 20
  • 32
  • +1 for very good answer and paying attention to this long question. As one alternative you propose creating another vector of pointers to the objects created. Well, all the objects created with CGameObjectFactory::create() I store in separate container, because those objects are then rendered on the screen. I of course delete in my code elsewhere. Should I keep another vector container, please? – Bunkai.Satori Mar 04 '11 at 03:35
  • That's ok if you have it stored in a container elsewhere, but then again you have to remember that std::vector does not call delete on the pointers, but deletes the memory allocated for the pointers: e.g. std::vector will only erase BaseObject*, not the actual memory pointed at, so you will have to loop through the vector and call delete on the objects. A better way of handling that would be to use std::shared_ptr since once the vector deletes the shared_ptr, it will automatically deallocate the memory for the object. I hope you understand what I mean :) Good luck. – Julien Lebot Mar 04 '11 at 08:50
  • thanks for your navigation. I have HEAP CORRUPTION error DETECTED. It is related to the memory allocated by the line: **return new Object();** in the code above. I decided to solve problem one by one, and for the time being, I was overlooking Debug Error! window, as I did not think it would relate to reported memory leaks. – Bunkai.Satori Mar 04 '11 at 20:05
  • All the answers were helpful and valuable. Your answer was exceptional in that you exactly showed me how to use **std::smart_ptr**. You even took the effort to rewrite my code into using **std::smart_ptr**. I am marking this answer as the *Accpetable Answer*. – Bunkai.Satori Mar 04 '11 at 20:54
1

As others have said, look into various implementations of shared_ptr.

But if you really want to do what you think your code is doing, your create and release methods should look more like this (you'll also need a vector to store the created BaseObject*s [called vObjects below] as your current code stores only the factories, not the created objects):

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
}

But then you should probably code a destructor to call ReleaseClassTypes:

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

And in a slight deviation to the Rule of Three, you'll probably want to make the copy constructor and assignment operator private to disallow copies (or you could properly define them to acquire new resources and release the old ones, but I'm not sure why you would need to copy a factory).

private:
CGameObjectFactory(const CGameObjectFactory& cgoFact) { }
CGameObjectFactory& operator=(const CGameObjectFactory& cgoFact) { return *this; }
user470379
  • 4,879
  • 16
  • 21
  • @user470379: +1 and thank you very much for your going deep into my problem. Well, I would prefer avoiding smart pointers, so I tried, to create another vObjects vector as you say. However, during compilation, I get compile error in the line of vObjects.push_back(); d:\source\satori\satori\gameobjectfactory.h(48): error **C2663: 'std::vector<_Ty>::push_back' : 2 overloads have no legal conversion for 'this' pointer**. I've spent virtually a whole day solving this, tried countless number of combinations, but no luck so far.. – Bunkai.Satori Mar 04 '11 at 16:38
  • I think, it should be obvious, that I have to store outptut from create in a vector outside CGameObjectFactory class. So why it does ont work, if I delete just those members? I delete them, ut I still get memory leaks generated by **return new Object();** – Bunkai.Satori Mar 04 '11 at 16:42
  • @Bunkai.Satori By itself in a bubble, I don't think this code should cause that error, so I'd have to see the surrounding code that you changed. By any chance is the `CGameObjectFactory` instance used to call the `create` function `const`? – user470379 Mar 04 '11 at 18:29
  • @Bunkai.Satori If I'm understanding you right, you're saying the code that calls `CGameObjectFactory::create` also has code (external to the class) to delete the new objects that `create` returns? If so, you shouldn't need this then, but that code could have a bug in it causing it not to delete them properly. Or is it possible you're slicing the `BaseObject*`s? http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c – user470379 Mar 04 '11 at 18:33
  • @user470379: thank you so much for paying attention to my problem. I am still fighting with it. To answer your question, in Resource Manager, I call instance of CGameObjectFactory **gameObjectFactory** to create new object as follows: **gameObjectRecord.pGameObject = gameObjectFactory.create(nObjectTypeID);** – Bunkai.Satori Mar 04 '11 at 19:02
  • @user470379: to answer your second question, yes, again in Resource Manger, I call Release function that traverses the container with pointers, and deletes them as follows: if( LayerContainerParam.vData[nLoop].pGameObject != NULL) **LayerContainerParam.vData[nLoop].pGameObject = NULL;** I think it is not crucial, I have paralax scrolling, so I keep data in multiple layers (containers) but I traverse each of them, and clear them all as above mentioned. – Bunkai.Satori Mar 04 '11 at 19:08
  • If you need more information, please, let me know, I I'll give you what you need. – Bunkai.Satori Mar 04 '11 at 19:11
  • @user470379: regarding Slicing (thanks for that link), I pass only pointers, I do not copy objects, so that sould not be my case. I use polymorphism, so yes, i use parent pointers to keep pointers of derived classed, but that sould be ok. – Bunkai.Satori Mar 04 '11 at 19:17
  • @user470379: thanks for your navigation. You are perfectly correct, that the pointer memory release is correct. I have HEAP CORRUPTION error DETECTED. It is related to the memory allocated by the line: **return new Object();** in the code above. I decided to solve problem one by one, and for the time being, I was overlooking Debug Error! window, as I did not think it would relate to reported memory leaks. – Bunkai.Satori Mar 04 '11 at 20:00
0

Proposed Solution no1: Using Internal Vector for Generated Pointers

- Not Yet Working

The advices above recommend two solutions. First of them is to create another internal vector to record all the pointers generated by: return new Object();

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

The vector should be then manually deleted like in this function:

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

I did according to advices, and tired many other combinations as well. However, I get compile errors:

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>          ]

Here is the modified CGameObjectFactory, resulting in the compile error. Any good tip on where is the problem now, please?

#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
Bunkai.Satori
  • 4,698
  • 13
  • 49
  • 77
  • For error `gameobjectfactory.h(48): error C2663: 'std::vector<_Ty>::push_back' : 2 overloads have no legal conversion for 'this' pointer` the method create is declared as const, thus cannot modify the class invariants in any way. The problem is that the push_back method is not const and will modify vObjects; this is not allowed in a const method. Solution: make the method non-const if you can. (remove the const at the end of the method create). – Julien Lebot Mar 14 '11 at 18:13
0

Proposed Solution no2: Using Smart Pointers

- Not Yet Working

The advices above recommend two solutions. The second solution recommends using smart pointers. I have replaced the key pointers with std::smart_ptr as the code below displays:

#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

The code gets compiled fine. However, I get error saying:

Debug Error!

HEAP CORRUPTION DETECTED: after Normal block(#949) at 0x04DC7E68.

CRT detected that the application wrote to memory after end of the heap buffer.

Memory allocated at: filepath to line in 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.

Bunkai.Satori
  • 4,698
  • 13
  • 49
  • 77
  • I could not reproduce the error you described, however I do get a debug assert in the method `RegisterClassType`. From what I understand, you resize the factory vector to store a new creation func. The problem with that is an index/size mismatch (index starts at 0 !). So when you try to access the `nObjectIDParam` of the vector, you are accessing one element BEYOND the vector size. I changed the method like this and it worked: `if(vFactories.size() < nObjectIDParam+1) vFactories.resize(nObjectIDParam+1);` – Julien Lebot Mar 14 '11 at 18:20