Jump to content
  • Advertisement
Sign in to follow this  
mikev

Verifying C++ Destructor Cleanup for regular pointers

This topic is 399 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I am working with an older game engine code base that uses regular * pointers (not smart pointers).

I want to verify that my destructor for a GameLevel objects is cleaning up all the memory that it allocated.

My first idea was:

measure memory used by process
new up, then delete a GameLevel object
measure memory again and make sure its the same

I don't know if functions exist to accurately measure the process memory. I'm using the SDL2 framework.

I could refactor the code and try to find every single place its using a * pointer but that could be quite a task...

Ideas and advice would be appreciated.

 

Share this post


Link to post
Share on other sites
Advertisement
This is a non-starter for various reasons. The way the heaps works, the process doesn't release the memory to the OS when a pointer is deleted. That's probably the most obvious obstacle. You also have the issue that other memory might have been allocated during the lifetime of the object. Either way: No.

Share this post


Link to post
Share on other sites

Is it an immediate problem? i.e. do you know that there are currently memory leaks? 

If so, identify where the issue is using the tools @[member='ApochPiQ'] mentioned, and refactor to smart pointers.

Otherwise, just make a note to clean up the code as you work on it in future.

Share this post


Link to post
Share on other sites

There is no compelling need of using smart pointers at all. Sure they help programmers but they also let programmers intend to go on the "allocate and forget about" track that may work in something like Java or C# but should not be approached in C++. We have the possibilities to manage what goes where in memory, on the stack and the heap so we are responsible for keeping an eye on it.

To simply keep track on your new/delete calls try either replacement new model with some kind of counter behind it or go directly to an allocater based approach like described here http://bitsquid.blogspot.de/2010/09/custom-memory-allocation-in-c.html

I'm using a similar system for over 2 years now and never had any problems with memory leaks while responsible allocators would have raised an error so I was able to fix it directly

Share this post


Link to post
Share on other sites

Whenever I'm writing old-school C-style memory management code in C++ these days (i.e. raw pointers, not 100% RAII, no "smart" anything), I now use a template like this that asserts when you forget to clean up properly. It also greatly helps to annotate which raw pointers are users of an object, and which raw pointer is the actual owner of an object:

template<class T> struct Owner 
{
	~Owner() { ASSERTMSG(p == 0, "Leak detected"); }
	void Release() { if(p) p->Release(); p = 0; }
	T* Relinquish() { T* copy = p; p = 0; return copy; }
	explicit Owner(T* data=0) : p(data) {}
	Owner(const Owner& o) : p(o.p) {o.p=0;}
	Owner<T>& operator=(const Owner& o) { ASSERTMSG(p == 0, "Leak detected"); p = o.p; o.p=0; return *this; }
	Owner<T>& operator=(T* data) { ASSERTMSG(p == 0, "Leak detected"); p = data; return *this; }
	operator Ptr<T>() const { return p; }
	T* operator->() const { ASSERT(p, "null dereference"); return  p; }
	T& operator *() const { ASSERT(p, "null dereference"); return *p; }
	operator bool() const { return !!p; }
	T* Raw() const { return p; }
private:
	mutable T* p;
};

//e.g.
struct Foo { 
  //int* m_A;//bad
  //int* m_B;//bad
  Owner<int> m_A;
  Ptr<int> m_B;
  Foo() { m_A = new int; }
  ~Foo() { delete m_A.relinquish(); }// if you forget to write this part, you'll get an assertion failure!
};

And I use this one for non-owning pointers to ensure that uninitialized pointer bugs don't ever happen:

template<class T> struct Ptr
{
	Ptr(T* data=0) : p(data) {}
	operator T*() const { return p; }
	T* operator->() const { ASSERTMSG(p, "null dereference"); return  p; }
	T& operator *() const { ASSERTMSG(p, "null dereference"); return *p; }
	operator bool() const { return !!p; }
	T* Raw() const { return p; }
private:
	T* p;
};

In optimized builds there's basically no overhead compared to actually using raw pointers, but in development builds they catch all sorts of bugs.
I've had similar templates forced onto me during several game projects in the past and I really didn't like them at the time... however, 5 years of fixing stupid bugs later and now I'm converted and will recommend this practice to others :D

I haven't looked into it fully, but I believe that the C++ Core Guidelines project supplies similar templates to mine (except probably much more robust!).

Edited by Hodgman

Share this post


Link to post
Share on other sites

They're on the way, yeah. I think the current advice (Sean would probably know a lot more about it) is to use unique_ptr for ownership and raw pointers otherwise, with the idea of assuming that raw pointers are non-owning, but that can easily go wrong if someone drops the ball. I'm hoping we see a lot of the new stuff soon.

Edited by Khatharr

Share this post


Link to post
Share on other sites

Kinda in response to Shaarigan, I have to depart on that assessment of smart pointers. If I'm not mistaken the whole point of their existence is to acknowledge that things can, and do happen in a large enough code base (exceptions, most notably) that prevent the otherwise predictable flow control of the application reaching a method, or destructor that would otherwise perform the raw memory management.

In response to OP, If the codebase is small enough, one option is just to 'Caveman' it. That is place a breakpoint at the point the application receives a QUIT message of some sort, and just step through the object deallocations. Obviously this can be very error prone, so I wouldn't recommend for any codebase of substantial size.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!