Dynamic allocation leaks memory? - c++

bool CPythonNonPlayer::LoadNonPlayerData(const char *c_szFileName)
{
DWORD dwElements;
TMobTable *pTable = (TMobTable *) zObj.GetBuffer();
for(DWORD i = 0; i < dwElements; ++i, ++pTable)
{
TMobTable *pNonPlayerData = new TMobTable;
memcpy(pNonPlayerData, pTable, sizeof(TMobTable));
m_NonPlayerDataMap.insert(TNonPlayerDataMap::value_type(pNonPlayerData->dwVnum, pNonPlayerData));
}
return true;
}
My question is: what am I doing wrong? This leaks a lot of memory. After each call of this function, the application usage increases by 10MB.

The problem is not in this function. The problem is in the way you handle m_NonPlayerDataMap. This function transfers ownership of some objects to that map, and it's that map's responsibility to delete them when it's done with them. I'll bet it doesn't.
By the way, to avoid this kind of problem just don't do this. Don't use new unless you really need to. Instead, make the map a map of values rather than a map of pointers. If you can't figure out any way to make that happen, at least use smart pointers rather than raw pointers.

Use smart pointer wrappers to handle memory management for you, eg:
If using a version earlier than C++11:
#include <memory>
// std::auto_ptr is not container-safe!
typedef std::map<DWORD, TMobTable*> TNonPlayerDataMap;
TNonPlayerDataMap m_NonPlayerDataMap;
...
bool CPythonNonPlayer::LoadNonPlayerData(const char *c_szFileName)
{
DWORD dwElements = ...;
...
// I'm assuming this just returns a pointer to an existing memory
// buffer and is not actually allocating a new buffer. If it is,
// you need to free it when you are done copying it...
//
TMobTable *pTable = (TMobTable *) zObj.GetBuffer();
for(DWORD i = 0; i < dwElements; ++i, ++pTable)
{
std::auto_ptr<TMobTable> pNonPlayerData(new TMobTable);
// don't use memcpy! better would be to give TMobTable a copy constructor instead...
// std::auto_ptr<TMobTable> pNonPlayerData(new TMobTable(*pTable));
//
*pNonPlayerData = *pTable;
// if successful, release local ownership of the object.
// if failed, ownership will remain here and free the object when the auto_ptr goes out of scope.
//
if (m_NonPlayerDataMap.insert(std::make_pair(pNonPlayerData->dwVnum, pNonPlayerData.get())).second)
pNonPlayerData.release();
}
return true;
}
Alternatively, if you are using C++11 or later:
#include <memory>
// std::unique_ptr is container-safe!
typedef std::map<DWORD, std::unique_ptr<TMobTable>> TNonPlayerDataMap;
TNonPlayerDataMap m_NonPlayerDataMap;
...
bool CPythonNonPlayer::LoadNonPlayerData(const char *c_szFileName)
{
DWORD dwElements = ...;
...
// I'm assuming this just returns a pointer to an existing memory
// buffer and is not actually allocating a new buffer. If it is,
// you need to free it when you are done copying it...
//
TMobTable *pTable = (TMobTable *) zObj.GetBuffer();
for(DWORD i = 0; i < dwElements; ++i, ++pTable)
{
std::unique_ptr<TMobTable> pNonPlayerData(new TMobTable);
//
// or, if using C++14 or later:
// std::unique_ptr<TMobTable> pNonPlayerData = std::make_unique<TMobTable>();
// don't use memcpy! better would be to give TMobTable a copy constructor instead...
// std::unique_ptr<TMobTable> pNonPlayerData(new TMobTable(*pTable));
// std::unique_ptr<TMobTable> pNonPlayerData = std::make_unique<TMobTable>(*pTable);
//
*pNonPlayerData = *pTable;
// if successful, ownership of the object is transferred into the map.
// if failed, ownership will remain here and free the object when the unique_ptr goes out of scope.
//
m_NonPlayerDataMap.insert(std::make_pair(pNonPlayerData->dwVnum, std::move(pNonPlayerData)));
}
return true;
}

Related

Is shared_ptr always free memory automatically?

You are partially replacing old code from the past. I'm replacing an existing pointer with a smart pointer for this operation, and I'm curious about deallocation. I replaced the code as below, will a leak occur?
std::vector<std::shared_ptr<Interface>> interface_list;
for (int i = 0 ; i < 5; ++i) {
Interface *buffer = CreateFactory(i);
if (buffer) interface_list.emplace_back(buffer);
}
...
for (std::shared_ptr<Interface> it: interface_list) {
it.reset();
}
//IS iT ok?
Assuming you fix the loop to call reset on the actual shared_ptrs instead of copies, the answer is "yes":
for (auto& it: interface_list) {
it.reset();
}
That leaves you with a list of dummy shared_ptr instances, of course.
You can also just clear the vector and have C++ reset the pointers for you:
interface_list.clear();
or just let interface_list go out of scope.

Heap corruption in deconstructor

I'm creating a program for decompiling some game script files. The latest part I've added is giving me some errors when dealing with dynamic arrays. This is the offending code:
typedef struct _COD9_ANIMREF_1
{
DWORD name;
DWORD reference;
};
typedef struct _COD9_USEANIM_1
{
WORD name; // offset of name
WORD numOfReferences; // reference count
WORD numOfAnimReferences; // reference count
WORD null1; // always null
DWORD* references = NULL; // dynamic array of references, amount = numOfReferences
_COD9_ANIMREF_1* animReferences = NULL; // dynamic array of references, amount = numOfAnimReferences
~_COD9_USEANIM_1()
{
if (references)
delete[] references;
if (animReferences) // program officially breaks here, if continued causes heap corruption
delete[] animReferences;
}
};
typedef struct _COD9_WORK_1
{
_COD9_GSC_1 Hdr;
char* data = NULL;
int* includes = NULL; //done
_COD9_USEANIM_1* usingAnim = NULL; //not done, heap corruption
_COD9_STRING_1* strings = NULL; //done
_COD9_FUNC_1* functions = NULL; //done
_COD9_EXTFUNC_1* extFunctions = NULL; //done
_COD9_RELOC_1* relocations = NULL; //done
~_COD9_WORK_1()
{
if (data)
delete[] data;
if (includes)
delete[] includes;
if (usingAnim)
delete[] usingAnim;
if (strings)
delete[] strings;
if (functions)
delete[] functions;
if (extFunctions)
delete[] extFunctions;
if (relocations)
delete[] relocations;
}
};
if (tstg.Hdr.numOfUsinganimtree)
{
tstg.usingAnim = new _COD9_USEANIM_1[tstg.Hdr.numOfUsinganimtree];
igsc.seekg(tstg.Hdr.usinganimtreeStructs);
for (int i = 0; i < tstg.Hdr.numOfUsinganimtree; i++)
{
_COD9_USEANIM_1 anim;
igsc.read(reinterpret_cast<char*>(&anim.name), sizeof(anim.name));
igsc.read(reinterpret_cast<char*>(&anim.numOfReferences), sizeof(anim.numOfReferences)); // this is 0 in this instance
igsc.read(reinterpret_cast<char*>(&anim.numOfAnimReferences), sizeof(anim.numOfAnimReferences));
igsc.read(reinterpret_cast<char*>(&anim.null1), sizeof(anim.null1));
anim.references = new DWORD[anim.numOfReferences]; // allocate 0 size array so theres something to delete
if(anim.numOfReferences) // should not be entered
{
igsc.read(reinterpret_cast<char*>(&anim.references), (anim.numOfReferences*sizeof(DWORD))); // if numOfReference = 0, function should return
}
anim.animReferences = new _COD9_ANIMREF_1[anim.numOfAnimReferences];
for (int ii = 0; ii < anim.numOfAnimReferences; ii++)
{
_COD9_ANIMREF_1 animref;
igsc.read(reinterpret_cast<char*>(&animref.name), sizeof(animref.name));
igsc.read(reinterpret_cast<char*>(&animref.reference), sizeof(animref.reference));
anim.animReferences[i] = animref;
}
tstg.usingAnim[i] = anim;
printf("anim: %d\n", i); // program reaches this
}
printf("Anims Done\n"); // program doesn't reach this
ReorderUsingAnim(&tstg);
}
Here is what is being read into the fields:
anim.name = 0x06BB
anim.numOfReferences = 0x0000
anim.numOfAnimReferences = 0x0001
anim.null1 = 0x0000
Where I think the error occurs is with the references array, since technically the size is 0 in this instance. But I'm not sure what to do about it, and I'm pretty lost in general about heap corruptions too.
_COD9_USEANIM_1 (why oh why newbies use such horrible names?? Is it enjoyable for them to call variables something like _Z_ASHD532___8AHQ_ ??) has two arrays (why not vectors??), references and anim_references. It has a destructor which frees the arrays if the pointers are not zero. But no constructor. This is DANGEROUS. You should, as a very least, provide a constructor which initializes them references and anim_references to zero. You also need the copy constructor. Remember the rule: if you provide one of the three (default constructor, destructor, copy constructor), you almost certainly need all three.
Ok, now you start your loop
for (int i = 0; i < tstg.Hdr.numOfUsinganimtree; i++)
In the loop you declare the variable anim
_COD9_USEANIM_1 anim;
You allocate its references and animReferences
anim.references = new DWORD[anim.numOfReferences];
...
anim.animReferences = new _COD9_ANIMREF_1[anim.numOfAnimReferences];
Finally you copy it to tstg.usingAnim
tstg.usingAnim[i] = anim;
You know what happens when you copy it? All fields are just copied. So now references and animReferences of tstg.usingAnim[i] point to the same address as references and animReferences of anim.
And then, the block ends. The evil computer calls the destructor for anim. The destructor calls delete[] for anim.references and anim.animReferences. But, references and animReferences of tstg.usingAnim[i] point to the same adresses. In other words, they now point to the array which were deleted.
Now the behaviour of your heap is unpredictable.
The best suggestion: forget arrays, and use vectors. You know, std::vector from the standard library.
Second best suggestion: provide default constructor and copy constructor. (PS: and assignment operator!)
(Note that you program may have other bugs too.)

Using malloc/free with classes

I have some code that allocates memory for classes using malloc/realloc and then deletes them again using free. Below is an extract from what I'm working with:
void NewSubInstances()
// Invoked by each worker thread to grow the subinstance array and
// initialize each new subinstance using a particular method defined
// by the subclass.
{
if (slavetotalspace >= totalobj) { return; }
//Remember current large size
G4int originaltotalspace = slavetotalspace;
//Increase its size by some value (purely arbitrary)
slavetotalspace = totalobj + 512;
//Now re-allocate new space
offset = (T *) realloc(offset, slavetotalspace * sizeof(T));
if (offset == 0)
{
G4Exception("G4VUPLSplitter::NewSubInstances()",
"OutOfMemory", FatalException, "Cannot malloc space!");
return;
}
//The newly created objects need to be initialized
for (G4int i = originaltotalspace; i < slavetotalspace; i++)
{
offset[i].initialize();
}
}
void FreeSlave()
// Invoked by all threads to free the subinstance array.
{
if (!offset) { return; }
free( offset);
offset = 0;
}
I know that malloc will not call the constructor of the class, but this is dealt with by the initialize function. My question is: how can I deal with the freeing of the memory in a way that will also call the destructor of the classes (the classes generally have dynamically allocated memory and will need to delete it)?
Thanks!
how can I deal with the freeing of the memory in a way that will also call the destructor of the classes (the classes generally have dynamically allocated memory and will need to delete it)?
The answer to your question looks like this:
void FreeSlave()
{
if (!offset)
{ return; }
// call destructors explicitly
for(G4int index = 0; index < slavetotalspace; ++index)
offset[index].~T();
free( offset);
offset = 0;
}
That said, DO NOT use malloc/realloc/free in C++ (no, not even if any excuse goes here). While it will probably work (not sure on this), this code is obscure/has non-obvious dependencies, is brittle, difficult to understand, and it violates the principle of least surprise.
Also, please do not call your pointer "offset" and do not use C-style casts in C++ (it's bad practice).
You may use :
void FreeSlave()
{
if (!offset) { return; }
for (G4int i = slavetotalspace; i != 0; --i) {
offset[i - 1].~T();
}
free(offset);
offset = 0;
}
I would recommend to have a destroy() method ... since you have already initialize() ... the way of calling the destructor, albeit that is allowed, is really ugly and feels like a hack (which is).

no need to delete struct memory in vector<StructA*>?

i saw some code like below, but i didn't see any delete statement, is there any memory leak problem?
struct RStatus
{
int fid;
int status;
};
void Foo()
{
vector<RStatus*> rsVec;
RStatus* rs = null;
rs = new RStatus(); // memory allocated here!
rs->fid = 0
rs->status = 0;
rsVec.push_back(rs);
}
If you use vector<RStatus*>, then you have to use delete, otherwise you will have a memory leak.
However, if you use vector<RStatus>, then you don't have to use delete — this is recommended1.
1. If you want to use pointers, then the recommendation is that you should be using smart pointers such as std::unique_ptr, or std::shared_ptr.
Yes, you should free your memory allocated :
struct RStatus
{
int fid;
int status;
};
void Foo()
{
vector<RStatus*> rsVec;
RStatus* rs = null;
rs = new RStatus(); // memory allocated here!
rs->fid = 0
rs->status = 0;
rsVec.push_back(rs);
// free :
unsigned int size = rsVec.size();
for (unsigned int i = 0; i < size; i++ )
delete rsVec[i]; // delete because you used new
}
If you don't do that, all the memory will never be released at the vector destruction.
I would suggest you to use std::vector<RStatus> instead of std::vector<RStatus*>.
You may also used smart ptr. You can found some documentation about it here : http://www.cplusplus.com/reference/memory/shared_ptr/
EDIT: As suggested in comments, if an exception is thrown at rsVec.push_back(rs), the memory allocated will be lost, that's why smart pointers would be a better solution. Or again, use std::vector<RStatus> instead.
Yes, there is a memory leak: the pointer to the created structure is lost after the vector is destroyed, and the memory is never released.
Unless someone performs a delete for each element of rsVec before clearing or destroying the vector.
Yes, that code leaks the RStatus.
It also does nothing else: possibly the real code's vector gets passed to some function that takes posession of the vector's contents.
Tracking down memory leaks is generally not a local problem: every use of that pointer has to, in theory, be examine to figure out if it leaks. Techniques like 'if I allocate it, delete it' and RAII (including smart pointers) attempt to make it more local, so you can tell from an incomplete program if there is a leak.
use boost::shared_ptr if you don't want to bother yourself with a deletion of allocated objects.
http://www.boost.org/doc/libs/1_54_0/libs/smart_ptr/shared_ptr.htm
struct RStatus
{
int fid;
int status;
};
void Foo()
{
vector<shared_ptr<RStatus> > rsVec;
shared_ptr<RStatus> rs = shared_ptr<RStatus>(); // empty shared_ptr
rs.reset(new RStatus()); // memory allocated here!
rs->fid = 0
rs->status = 0;
rsVec.push_back(rs); // shared_ptr is copied
}// vector is destroyed and shared_ptrs also
be careful however not to mixed up things using both shared_ptr and ordinary, raw pointers to avoid situation when shared_ptr tries to delete object which is already deleted

Safe to return a vector populated with local variables?

Is it safe to return a vector that's been filled with local variables?
For example, if I have...
#include <vector>
struct Target
{
public:
int Var1;
// ... snip ...
int Var20;
};
class Test
{
public:
std::vector<Target> *Run(void)
{
std::vector<Target> *targets = new std::vector<Target>;
for(int i=0; i<5; i++) {
Target t = Target();
t.Var1 = i;
// ... snip ...
t.Var20 = i*2; // Or some other number.
targets->push_back(t);
}
return targets;
}
};
int main()
{
Test t = Test();
std::vector<Target> *container = t.Run();
// Do stuff with `container`
}
In this example, I'm creating multiple Target instances in a for loop, pushing them to the vector, and returning a pointer to it. Because the Target instances were allocated locally, to the stack, does that mean that the returned vector is unsafe because it's referring to objects on the stack (that may soon be overwritten, etc)? If so, what's the recommended way to return a vector?
I'm writing this in C++, by the way.
Elements get copied when you push_back them into a vector (or assign to elements). Your code is therefore safe – the elements in the vector are no references to local variables, they are owned by the vector.
Furthermore, you don’t even need to return a pointer (and never handle raw pointers, use smart pointers instead). Just return a copy instead; the compiler is smart enough to optimise this so that no actual redundant copy is made.