Jump to content
  • Advertisement
Sign in to follow this  
Programmer16

Smart pointer classes

This topic is 4838 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 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!

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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 references

float* 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

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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

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!