Smart Pointers Revealed!

Published August 17, 2005
Advertisement
I finally cleaned up all the excessive debug messages in the smart pointers and now I just have a bunch of log calls :).

Either way, here is my current solution to smart pointers.

#ifndef CMN_MEMORY_H#define CMN_MEMORY_H#include #include #include "garbage_collector.h"#include "logger.h"#define DEBUG_MEMPTRtemplate <typename T>class mem_ptr{private:	static std::mapint
> m_mapActive;
T* m_tObj;

void addRef(void);
void remRef(int bForceDelete = false);
public:
mem_ptr() : m_tObj(NULL) {};

mem_ptr(T* tObj) : m_tObj(tObj)
{
addRef();
}

mem_ptr(const mem_ptr &p) : m_tObj(p.m_tObj)
{
addRef();
}

~mem_ptr()
{
remRef(true); // Force the delete to clean up anything not sent to the garbage collector
}

inline void release(void)
{
*this = NULL;
#ifdef DEBUG_MEMPTR
CLogger::Write(COMMENT, "Object of type %s was released.", typeid(m_tObj).name());
#endif
}

inline bool isValid() const
{return (m_tObj!=0);}

inline T* operator =(T *o);

inline T* operator =(const mem_ptr &p);

inline operator T*() const {return m_tObj;}

inline bool operator !() {return !(m_tObj);}

inline bool operator ==(const mem_ptr &p) const
{return (m_tObj==p.m_tObj);}

inline bool operator ==(const T* o) const
{return (m_tObj==o);}

inline T* operator ->() const
{return m_tObj;}
};


template <typename T>
std::mapint> mem_ptr::m_mapActive;


template <typename T>
void mem_ptr::addRef(void)
{
if( !m_tObj ) return;

m_mapActive[m_tObj]++;

#ifdef DEBUG_MEMPTR
CLogger::Write(COMMENT, "Reference increased on object of type %s. Reference count is now %d", typeid(m_tObj).name(), m_mapActive[m_tObj]);
#endif
}


template <typename T>
void mem_ptr::remRef(int bForceDelete)
{
if ( !m_tObj ) return;

m_mapActive[m_tObj]--;

#ifdef DEBUG_MEMPTR
CLogger::Write(COMMENT, "Reference decreased on object of type %s. Reference count is now %d", typeid(m_tObj).name(), m_mapActive[m_tObj]);
#endif

if( m_mapActive[m_tObj] <= 0 )
{
m_mapActive.erase(m_tObj);

#ifdef DEBUG_MEMPTR
CLogger::Write(COMMENT, "Object of type %s was removed from the active list", typeid(m_tObj).name());
#endif
/* If the delete is forced, then
the object is destroyed immediately
otherwise, it is sent to the GarbageCollector
for a one time cleaup with the other objects. */

if (bForceDelete)
{
delete m_tObj;
m_tObj = 0;

#ifdef DEBUG_MEMPTR
CLogger::Write(WARNING, "Object of type %s was killed by mem_ptr::remRef()", typeid(m_tObj).name());
#endif
} else {
GarbageCollector::Recycle(m_tObj);

#ifdef DEBUG_MEMPTR
CLogger::Write(COMMENT, "Object of type %s was sent to the Garbage Collector", typeid(m_tObj).name());
#endif
}
}
}


template <typename T>
inline T* mem_ptr::operator =(T *o)
{
remRef();
m_tObj=o;
addRef();
return m_tObj;
}


template <typename T>
inline T* mem_ptr::operator =(const mem_ptr &p)
{
remRef();
m_tObj=p.m_tObj;
addRef();
return m_tObj;
}


#endif










I know this isn't extremely well commented, but hopefully you get the idea. Besides, it's almost 2:00 A.M. and I'm about to pass out.

I will note, however, that the main reason for this design is to make the storage maps smaller by keeping them grouped by object type through templates. This makes traversing them a bit faster when needing to add a new reference. Also be aware that the overhead of std maps is not light. I intend on implementing hash tables/sets, lists, and other data structures later on to help with this a bit.

I also wrote a handy little console test app to log all of my pointer assignments and check for memory leaks:

int main(void){	// Check for memory leaks at the end of the application.	// Results will show in the Output window.	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );	mem_ptr Logger = new CLogger("ErrorLog.html");	Logger->Write(COMMENT, "#0000BB\">Beginning First Test");	Logger->Write(COMMENT, "Allocating and Creating iTest1");	mem_ptr<int> iTest1 = new int;	Logger->Write(COMMENT, "Creating iTest1_Copy");	mem_ptr<int> iTest1_Copy;	Logger->Write(COMMENT, "Copying iTest1 to iTest1_Copy");	iTest1_Copy = iTest1;	Logger->Write(COMMENT, "#0000BB\">Ending First Test");	Logger->Write(COMMENT, "#0000BB\">Beginning Second Test");	Logger->Write(COMMENT, "Creating iTest2");	mem_ptr<int> iTest2;	Logger->Write(COMMENT, "Allocating iTest2");	iTest2 = new int;	Logger->Write(COMMENT, "Creating and Allocating iTest2_Copy");	mem_ptr<int> iTest2_Copy = new int;	Logger->Write(COMMENT, "Assigning iTest2_Copy to iTest2.This should dereference iTest2_Copy's initialized allocation and reassign it");	iTest2_Copy = iTest2;	Logger->Write(COMMENT, "#0000BB\">Ending Second Test");	// Intentionally neglect to release 2 of the objects	// to verify the app is still killing them.  This	// will generate warnings.	iTest1.release();	iTest2_Copy.release();	Logger.release();	GarbageCollector::CleanUp();	Logger->Write(COMMENT, "#0000BB\">EXITING APPLICATION");	/* Return error free */	return 0;}


Here is what this log looks like:



The reason for the warnings at the bottom is that I am testing to make sure that the objects get destroyed regardless of the Garbage Collector getting ahold of them. If the last reference to the object goes out of scope before it is released and inserted into the Garbage collector, then the smart pointer is forced to delete the object during destruction. This is not preferred and is why you see a warning when I fail to release iTest1_Copy and iTest2. Also note that the smart pointers are cleaning up for me even once I return from main(); This is noted in the log after "EXITING APPLICATION".

The reason the logger continues to work is because I made it static for testing purposes. It will be a singleton when implemented and will no longer be created as a mem_ptr.

As you can see, I haven't gotten around to implementing the XML/JS version of the logger yet. This is just a place holder for the mean time.

Feel free to let me know what you think; Love it, hate it, whatever.

Thanks again for looking!
0 likes 1 comments

Comments

Programmer16
Wow, that looks very neat and clean (alot better than my smart pointer class.) Keep up the good work!
August 17, 2005 09:20 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement