Smart pointer classes

Started by
7 comments, last by stylin 18 years, 8 months ago
I created two smart pointer classes last night. I also tested them, but I think I'm doing something wrong (mine seems to be faster than the regular array allocation/deallocation.) I ran a test, allocating 100000 of my Interface class (a class wrapping an integer.) I averaged the results: DEFAULT: 157 156 156 156 156 ----- 156.2 DFTLIB: 140 125 125 125 125 --- 128 I don't really understand how this is possible since my class uses the default new/delete and everything. Here's my test code:

// This is my test class. I also tried it with several other variables inside of it.)
class Interface
{
public:
	int integer;
};

//------------------------------------------------------------------------
// WinMain()
//------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
	//------------------------------------------------------------------------
	// Memory leak detector
	//------------------------------------------------------------------------
	_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF);
	_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
	_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);

	//------------------------------------------------------------------------
	// Profiler variable
	//------------------------------------------------------------------------
	unsigned int nStart = GetTickCount();

	//------------------------------------------------------------------------
	// Test of default alloc/deallocation
	//------------------------------------------------------------------------
	/*std::vector<Interface*> Vector;
	for(unsigned int nIndex = 0; nIndex < 100000; ++nIndex)
	{
		Interface* pInt = new Interface;
		Vector.push_back(pInt);
	}

	for(nIndex = 0; nIndex < 100000; ++nIndex)
	{
		Interface* pInt = Vector.at(nIndex);
		delete pInt;
	}
	Vector.clear();*/

	//------------------------------------------------------------------------
	// Test of dftlib::SmartPtr alloc/deallocation.
	//------------------------------------------------------------------------
	std::vector< dftlib::SmartPtr<Interface>*> Vector;
	Vector.reserve(100000);

	for(unsigned int nIndex = 0; nIndex < 100000; ++nIndex)
	{
		dftlib::SmartPtr<Interface> SmtPtr;
		SmtPtr.Assign(new Interface);
		Vector.push_back(&SmtPtr);
	}

	for(nIndex = 0; nIndex < 100000; ++nIndex)
	{
		dftlib::SmartPtr<Interface>** ppInt = &Vector.at(nIndex);
		(*ppInt)->Release();
	}

	Vector.clear();

	//------------------------------------------------------------------------
	// End of profiler
	//------------------------------------------------------------------------
	unsigned int nEnd = GetTickCount();
	unsigned int nDif = nEnd - nStart;
	char Msg[16];
	itoa(nDif, Msg, 10);
	
	MessageBox(0, Msg, "Profile", MB_OK);
	return 0;
}


And my SmartPtr and ComSmartPtr classes:

//--------------------------------------------------------------------------
// This code is part of my(Donald Beals) personal codebase. If you use part
// of it (either in your freeware/shareware/commercial/codebase/library
// please give me credit. I spent a lot of time on it. Thanks!
//--------------------------------------------------------------------------
#ifndef _dftlib_SmartPointers_h_
#define _dftlib_SmartPointers_h_

//--------------------------------------------------------------------------
// Header files
//--------------------------------------------------------------------------

//--------------------------------------------------------------------------
// Start of the dftlib namespace
//--------------------------------------------------------------------------
namespace dftlib
{
	//--------------------------------------------------------------------------
	// Name: SmartPtr
	// Info: Maintains the lifecycle of a pointer.
	// Date: 8/18/2005
	// Author: Donald Beals (Programmer16)
	//--------------------------------------------------------------------------
	template <typename PTRTYPE> class SmartPtr
	{
		PTRTYPE*	m_pObject;
	public:
		SmartPtr()
		{
			m_pObject = 0;
		}
		
		SmartPtr(PTRTYPE* pObj)
		{
			m_pObject = pObj;
		}
		
		~SmartPtr()
		{
			Release();
		}
		
		PTRTYPE* Assign(PTRTYPE* pObj)
		{
			m_pObject = pObj;
			return m_pObject;
		}
		
		bool IsValid() const
		{
			return m_pObject != 0;
		}
		
		void Release()
		{
			if(m_pObject)
			{
				delete m_pObject;
				m_pObject = 0;
			}
		}
		
		PTRTYPE* GetObject()
		{
			return m_pObject;
		}
		
		PTRTYPE** GetObjectPtr()
		{
			return &m_pObject;
		}
		
		PTRTYPE* operator ->()
		{
			return m_pObject;
		}
		
		PTRTYPE* operator =(PTRTYPE* pObj)
		{
			m_pObject = pObj;
			return m_pObject;
		}
	};
	
	//--------------------------------------------------------------------------
	// Name: ComSmtPtr
	// Info: Maintains the lifecycle of a COM interface.
	// Date: 8/18/2005
	// Author: Donald Beals (Programmer16)
	//--------------------------------------------------------------------------
	template <typename PTRTYPE> class ComSmtPtr
	{
		PTRTYPE	m_pObject;
	public:
		ComSmtPtr()
		{
			m_pObject = 0;
		}
		
		ComSmtPtr(PTRTYPE pObj)
		{
			m_pObject = pObj;
		}
		
		~ComSmtPtr()
		{
			Release();
		}
		
		PTRTYPE Assign(PTRTYPE pObj)
		{
			m_pObject = pObj;
			return m_pObject;
		}
		
		bool IsValid() const
		{
			return m_pObject != 0;
		}
		
		void Release()
		{
			if(m_pObject)
			{
				m_pObject->Release();
				m_pObject = 0;
			}
		}
		
		PTRTYPE GetObject()
		{
			return m_pObject;
		}
		
		PTRTYPE* GetObjectPtr()
		{
			return &m_pObject;
		}
		
		PTRTYPE operator ->()
		{
			return m_pObject;
		}
		
		PTRTYPE operator =(PTRTYPE pObj)
		{
			m_pObject = pObj;
			return m_pObject;
		}
		
		PTRTYPE operator[](unsigned int nIndex)
		{
			return m_pObject[nIndex];
		}
	};
}
//--------------------------------------------------------------------------
// End of the dftlib namespace
//--------------------------------------------------------------------------
#endif


Any ideas? Any suggestions? Thanks!
Advertisement
I've noticed that you're using the default copy constructor (none defined). I'm curious as to the reason for this. Do you not allow

SmartPtr< float > spFloat1 = new float( 3.4 );SmartPtr< float > spFloat2( spFloat1 );

? Both your pointers will pointing to the same object when you try to delete them.

Also, you're not checking for self assignment, which might speed it up - could be wrong though.

:stylin:

EDIT: I just noticed your checking for NULL when you Release(), so never mind the first part of this post.
:stylin: "Make games, not war.""...if you're doing this to learn then just study a modern C++ compiler's implementation." -snk_kid
Quote:Original post by stylin
I've noticed that you're using the default copy constructor (none defined). I'm curious as to the reason for this. Do you not allow

SmartPtr< float > spFloat1 = new float( 3.4 );SmartPtr< float > spFloat2( spFloat1 );

? Both your pointers will pointing to the same object when you try to delete them.

Also, you're not checking for self assignment, which might speed it up - could be wrong though.

:stylin:

EDIT: I just noticed your checking for NULL when you Release(), so never mind the first part of this post.


Thanks, I didn't think of that.

This may sound stupid, but what do you mean by 'self assignment'?
Thanks!
Basically the same situation as the copy constructor:

PTRTYPE* Assign(PTRTYPE* pObj) {   m_pObject = pObj; // if these are already equal,   return m_pObject;    // then you must be careful of object lifetimes}                       // and multiple referencesfloat* pFloat = new float( 3.4 );SmartPtr< float > spFloat = pFloat;spFloat.Release();cout << pFloat;     // bad

If you pass a pointer this function, your smart pointer will now point to the same address. If you delete your smart pointer, the original is now 0, so you must be careful. Also, was not creating a copy constructor intentional? Since your checking for NULL when you Release(), that's fine - I'm just curious.

:stylin:

EDIT: speed typos
:stylin: "Make games, not war.""...if you're doing this to learn then just study a modern C++ compiler's implementation." -snk_kid
Quote:Original post by stylin
Basically the same situation as the copy constructor:

PTRTYPE* Assign(PTRTYPE* pObj) {   m_pObject = pObj;   return m_pObject;}

If you pass a pointer this function, your smart pointer will now point to the same address. If you delete your smart pointer, the original is now 0, so you must be careful. Also, was not creating a copy constructor intentional? Since your checking for NULL when you Release(), that's fine - I'm just curious.

:stylin:


Ah, now I understand. No, it wasn't intentional. I added one, and I'm testing it now.

Thanks!

Ok, I did run into a problem like you said. When I try my copy constructor, they point to the same memory address, but it tries to get deleted twice (when I delete original, the smart pointer still points to it (is not 0.)) I'll work on it tomorrow (I have to work in a few hours.)

Thanks!
Self-assignment is a major concern:

SmartPtr< float > spFloat1 = new float( 3.4 );spFloat1 = spFloat1;  // nonsensicle here, but may be the result of a complex expression that is hard to detect

What happens is spFloat1 deletes (or removes a reference from) the object it is currently pointing to, then points to that garbage address. When you delete this object (either explicitly with SmartPtr<x>.Release(), or implicitly in your destructor) ... well you know.

:stylin:

EDIT: noticed you figured that out after my post
EDIT2: the effect is identical with reference counting, but you'll only find out until you delete the last reference.
:stylin: "Make games, not war.""...if you're doing this to learn then just study a modern C++ compiler's implementation." -snk_kid
Ok, thanks. I'll think on this tonight and tomorrow and I'll see what I can come up with. But, I've been looking at other implementations, and was wondering some sort of garbage list would work better? Say a vector, hash, or something else? But to do this I'd need some sort of identifier or pointer testing.

Thanks for your help!
You have a number of options:

1. Have your object keep track of its own references. Enginuity employs this technique.
2. Have your smart pointer count references, through a reference_count object. The details for this are complicated (at least for me) so I don't have much information on this.

:stylin:

EDIT: Added link to relevant Enginuity article
:stylin: "Make games, not war.""...if you're doing this to learn then just study a modern C++ compiler's implementation." -snk_kid

This topic is closed to new replies.

Advertisement