Code Of The Day
Submitted by Bernhard Gl?k, posted on 27 August 2001


Smart Pointer Template Class, by Bernhard Gl?k: Download: Pointer.h


This code snippet is from my upcoming book "Game Engine Design and Implementation using DirectX 8.1" and the contained "Realspace" game engine. It's a smart pointer template that has undergone a lot of testing. It mirrors the behaviour of normal pointers as closly as possible, however does not support the following things:

*Pointers to base classes in class hierarchies
*Never create a smartpointer from an object allocated on the stack like Pointer p( &someClass ); because it could crash your programm since the memory would be freed as soon as the Pointer goes out of focus

Everything else should work fine, and allows you to have multiple references to the same object which is automatically deleted when no objects refer it anymore. Also the class frees you of the troubles of NULL Pointer checking since that is done for you ( slows everything down, but you could blend it out in release builds )

You can use this code at your absolute free will..

Hope anyone finds it useful!
Bernhard Gl?k

PS: I would like to hear suggestions, bug fixes or anything, and I am looking for some skilled graphics artists willing to help me out on my example game using the "Realspace" Engine (it's a space game")





// -----------------------------------------------------------------------------
// File contents / class description:
// -----------------------------------------------------------------------------
//
// Smart pointer template class
//
// -----------------------------------------------------------------------------
// Originally created on 05/15/2000 by Bernhard Glueck
// Email: jglueck@vol.at
// You can use this software at your own free will, however I am not to be held
// responsible for any damage this does to you or to your health :-)
// You can freely use the code provided in your own programs, however if you find
// any bugs, or add a cool new feature to it, I would like to hear about it.
// -----------------------------------------------------------------------------

#ifndef	__POINTER_H__
#define __POINTER_H__

	/// Disable UDT warning
	#pragma warning ( disable : 4284 )

// -----------------------------------------------------------------------------
	/// Error handling macro only for this version
	#define ErrorReport( a ) 

	/// A simple macro for relasing C++ classes safely
	#define SafeRelease( object ) if ( object ) delete object

	/// SmartPointer template which offers automatic reference counting
	template <class T>
	class Pointer
	{
		public:

		struct PointerData
		{
			T *		data;
			int		count;
		};

		/// Assigns a native pointer
		inline void			operator = ( T * p );

		/// Assigns another smartpointer
		inline void			operator = ( const Pointer &p );

		/// Compares two smartpointers
		inline bool			operator == ( const Pointer &p );

		/// The same only negative
		inline bool			operator != ( const Pointer &p );

		/// Compares a smartpointer and a native pointer
		inline bool			operator == ( const T * p );

		/// Compares a smartpointer and a native pointer, negative
		inline bool			operator != ( const T * p );

		/// Accesses the native pointer
		inline const T *	operator -> ()  { return Get(); }

		/// Dereferences the native pointer
		inline T &			operator *	() { return *Get(); }

		/// Checks if the native pointer is valid
		inline bool			Valid();

		/// Converts the pointer to native
		inline operator const T * () { return Get(); }

		/// Releases the referenced object (safe)
		inline void			Release();

		/// Retrieves the native pointer...
		inline T *			Get();

		/// Increases the reference count by one, use with extreme CAUTION!
		inline void			AddRef();

		// Construction/Destruction
		inline	Pointer( T * p = NULL );
		inline	Pointer( const Pointer & );
		inline	~Pointer();

		private:

		inline void			Set( T * p );

		// Data members
		PointerData *		m_pointer;
	};

// -----------------------------------------------------------------------------
	template <class T>
	inline Pointer<T>::Pointer( T * p )
	{
		m_pointer = NULL;
		Set(p);
	}

// -----------------------------------------------------------------------------
	template <class T>
	inline Pointer<T>::Pointer( const Pointer &p )
	{
		m_pointer = p.m_pointer;
		m_pointer->count ++;
	}

// -----------------------------------------------------------------------------
	template <class T>
	inline Pointer<T>::~Pointer()
	{
		Release();
	}

// -----------------------------------------------------------------------------
	template <class T>
	inline bool Pointer<T>::operator != ( const Pointer &p )
	{
		if ( m_pointer != p.m_pointer ) return true;
		else return false;
	}

// -----------------------------------------------------------------------------
	template <class T>
	inline bool Pointer<T>::operator == ( const Pointer &p )
	{
		if ( m_pointer == p.m_pointer ) return true;
		else return false;
	}

// -----------------------------------------------------------------------------
	template <class T>
	inline bool Pointer<T>::operator != ( const T * p )
	{
		if ( m_pointer )
		{
			if ( m_pointer->data == p ) return true;
			else return false;
		}
		else return true;
	}

// -----------------------------------------------------------------------------
	template <class T>
	inline bool Pointer<T>::operator == ( const T * p )
	{
		if ( m_pointer )
		{
			if ( m_pointer->data != p ) return false;
			else return true;
		}
		else return false;
	}

// -----------------------------------------------------------------------------
	template <class T>
	inline void Pointer<T>::operator = ( T * p )
	{
		Set( p );
	}

// -----------------------------------------------------------------------------
	template <class T>
	inline void Pointer<T>::operator = ( const Pointer &p )
	{
		Release();

		m_pointer = p.m_pointer;
		m_pointer->count ++;
	}

// -----------------------------------------------------------------------------
	template <class T>
	inline void Pointer<T>::Set( T * p )
	{
		Release();

		m_pointer = new PointerData();
		m_pointer->data = p;
		m_pointer->count = 1;
	}

// -----------------------------------------------------------------------------
	template <class T>
	inline void Pointer<T>::AddRef()
	{
		if ( m_pointer )
			m_pointer->count ++;
	}

// -----------------------------------------------------------------------------
	template <class T>
	inline void Pointer<T>::Release()
	{
		if ( m_pointer )
		{
			m_pointer->count --;

			if ( m_pointer->count == 0 )
			{
				if ( m_pointer->data )
				{	
					delete m_pointer->data;
					m_pointer->data = NULL;
				}

				delete m_pointer; // This can only happen when no objects still refer it
			}

			// Very important
			m_pointer = NULL;
		}
	}

// -----------------------------------------------------------------------------
	template <class T>
	inline T* Pointer<T>::Get()
	{
		if ( m_pointer )
		{
			if ( m_pointer->data )
			{
				return m_pointer->data;
			}
			else 
			{	
				ErrorReport("Trying to access NULL pointer");
				return NULL;
			}
		}
		else 
		{	
			ErrorReport("Pointer access error");
			// Only for syntax, should never happen!
			return NULL;
		}
	}
// -----------------------------------------------------------------------------
	template <class T>
	inline bool Pointer<T>::Valid()
	{
		if ( m_pointer )
		{
			if ( m_pointer->data )
			{
				return true;
			}
			else return false;
		}
		else return false;
	}

// -----------------------------------------------------------------------------

#endif // __POINTER_H__

// -----------------------------------------------------------------------------
// Pointer.h - End of file
// -----------------------------------------------------------------------------



This code was submitted by Bernhard Gl?k, posted on 27 August 2001. To submit your own code, visit here.

The source code provided on this page is for educational purposes. Unless the author stated clearly the conditions under which you may use this code, please contact him/her for further information regarding the usage of this code for your own purposes. Authors submitting code, please be aware of this arrangement before doing so.




[prev]
Code Of The Day Collection

[next]


 


Reader Comments:

Please read our forum guide before using these forums.

    Re: Smart Pointer Template Class
 
I have a few of suggestions:
1) Include an allocator template parameter. This allows the smart pointer to work for objects that weren't created with new. For example, I might want to use a smart pointer for an object allocated with malloc, or an object allocated externally in another dll.
2) Include a template parameter encapsulating the reference counting mechanism. This way the client could override the smart pointer to work with objects that use an internal reference count. They could also make the smart pointer thread-safe by placing locks around count access.
3) Include a templated conversion operator. What if I wanted to convert a pointer of one class to the pointer of a different but compatible class? You could also include a template parameter that encapsulates the conversion process (e.g. it would convert with static_cast, dynamic_cast, or something else).



Justin Wilder

Other Posts By This Author

08-27-2001 - 03:11 PM

post reply - up-thread - alert

 
 
 
    Re: Smart Pointer Template Class by Justin Wilder
08-27-2001 - 03:11 PM
       Re: Smart Pointer Template Class by Gluber
08-27-2001 - 04:05 PM
              Re: Smart Pointer Template Class by Justin Wilder
08-27-2001 - 06:43 PM
 
 
 

  
 
I think you have problem with this operator.




template <class T>
inline void Pointer::operator = ( const Pointer &p )
{
Release();

m_pointer = p.m_pointer;
m_pointer->count ++;
}




Imagine the case.




test = new Test;
Pointer<Test> ref = test;
ref = test;




Here we set the Pointer to test, but when we try to set it to the same value again and the Pointer only has one reference attached the data will be deleted before we set the value again. Granted this is not a common case, but could occur.



flight

Other Posts By This Author

08-27-2001 - 04:12 PM

post reply - up-thread - alert

 
 
 
    Re: Smart Pointer Template Class by flight
08-27-2001 - 04:12 PM
       Re: Smart Pointer Template Class by Gluber
08-27-2001 - 04:24 PM
 
 
 

  
 
Why the check if a pointer is null before calling delete on that pointer? C++ guarantees that delete can safely be called on a null pointer, so in fact you're making an unnecessary check.

HTH,
Erik



JungleJim

Other Posts By This Author

08-27-2001 - 04:34 PM

post reply - up-thread - alert

 
 
 
    Re: Smart Pointer Template Class by JungleJim
08-27-2001 - 04:34 PM
       Re: Smart Pointer Template Class by Gluber
08-27-2001 - 05:26 PM
 
 
 

  
 

#define SafeRelease( object ) if ( object ) delete object




Even if you want to define that macro, never do it like that. You might accidentally write something like

SafeRelease(p);

else
blah();




And the compiler would never complain, yet it's a blatant syntax error. Plus, lack of parentheses when using macro parameters will get you in trouble, so get used to using them ALL the time. Try something like this.


#define SafeRelease( object ) do if ( object ) delete (object); while (0)




(actually I'm not sure if parentheses are acceptable with delete, I guess they should but I'm not 100% on this).




Greets
  Javier Arevalo
  http://www.terra.es/personal3/jare70



Jare

Other Posts By This Author

08-27-2001 - 05:58 PM

post reply - up-thread - alert

 
 
 
    Re: Smart Pointer Template Class by Jare
08-27-2001 - 05:58 PM
       Re: Smart Pointer Template Class by JCAB
08-27-2001 - 06:15 PM
              Re: Smart Pointer Template Class by Alberto Garc?-Baquero Vega
08-27-2001 - 06:34 PM
                     Re: Smart Pointer Template Class by Jare
08-27-2001 - 06:49 PM
       Re: Smart Pointer Template Class by Gluber
08-27-2001 - 07:45 PM
 
 
 

  
 
Why not to get rid of those nasty #defines and use inline functions?
template<class T> inline void DeleteIfNotZero(T* ptr)

{
if (ptr != 0)
delete ptr;
}



[I don't like cryptic names like SafeRelease]



Maciej Sinilo

Other Posts By This Author

08-27-2001 - 07:35 PM

post reply - up-thread - alert

 

  
 
IMHO, you should be more consistent in your use of const.

For example:




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




sounds wrong to me. This will only allow you to run const member functions of the reference counted class.

You probably want in this case:




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







Salutaciones,
JCAB

The Rumblings are back. Tell me what you think.



JCAB

Other Posts By This Author

08-27-2001 - 09:36 PM

post reply - up-thread - alert

 

  
 
As JCAB pointed out (can we get a first name, please?), your use of const is not consistent, but that doesn't mean you shouldn't use it. Instead, define different functions for const and non-const access. I.e.:



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



This way, access to the smart pointer implicitly mimics access to the actual pointer. This makes it easier to use the smart pointer directly in place of a regular pointer. This type of access is used all over the STL, and - more importantly - it's consistent.

As a side note, inline is unnecessary for implementations placed in the class declaration, it's implicit.



Justin Wilder

Other Posts By This Author

08-28-2001 - 12:34 AM

post reply - up-thread - alert

 
 
 
    Re: Smart Pointer Template Class by Justin Wilder
08-28-2001 - 12:34 AM
       Re: Smart Pointer Template Class by JCAB
08-28-2001 - 03:20 PM
 
 
 

  
 
Sorry to pick more nits, but there's something I forgot in my last post. You should replace your comparison operators with:



inline bool operator == ( const Pointer &p ) const;
inline bool operator != ( const Pointer &p ) const;
inline bool operator == ( const T * p ) const;
inline bool operator != ( const T * p ) const;



Without the const at the end, you wouldn't be able to access the comparison operators unless you have non-const access to it, which is obviously unnecessary. In addition, you could also remove the Pointer& comparison completely since you already define a conversion operator for Pointer to T*.

Oh yeah, you should probably make PointerData protected. Phew. Sorry.



Justin Wilder

Other Posts By This Author

08-28-2001 - 12:43 AM

post reply - up-thread - alert

 

  
 
Okay:

1) Implement your methods EXACTLY in the SAME ORDER as you declare them in the class. Otherwise it's hard to find where each method has been implemented.

2) Can you point out some non-standard compliler that crashes when you call delete to 0-pointer? I have used BCB, GNU C++, Watcom C++ and MSVC and they all work fine.

3) What's the point of having this PointerData struct allocated for each pointer? If you construct two pointers from the same object without copy-constructor of the pointer, it'll make your application to crash and you have no way to avoid that. It's not enough if you try to share same counter between pointers, because you don't always have access to the counter at all (see adapter pattern for instance).

4) It's slow to access objects through two indirect references. It also consumes relatively much memory (though you can overload the new of PointerData and have some pool for it), because iirc, dynamic allocation allocates atleast 8 extra bytes / object in msvc.

Cheers, Altair




"Only two things are infinite, the universe and human stupidity, and I'm not sure about the former." - Albert Einstein



Altair

Other Posts By This Author

08-28-2001 - 03:29 AM

post reply - up-thread - alert

 

  
 
Just some ideas:

1. The smart pointer is checked for validy with .Valid (IsValid is more obvios but this is not an issue) why dont you put a "bool operator!" to be more intuitive, I saw this in More Effective C++.

2. Why dont you make this macros a template function?
It will be more C++ than C style, i can be traced, will be type safe i.e. will lack all the disadvantages of the macros...

template
SafeRelease(T* pointer)
{
if ( pointer )
delete pointer;
}

3. Why dont you put some "pointer = NULL" in the SafeRelease macro?
Consider the case

SafeRelease(pointer)
SafeRelease(pointer)

The second SafeRelease is obviosly not safe at all...
For p.2 my template will become:

template
SafeRelease(T*& pointer)
{
if ( pointer )
delete pointer;
pointer = NULL;
}

Kind regards,
warjo



warjo

Other Posts By This Author

08-28-2001 - 04:20 AM

post reply - up-thread - alert

 
 
 
    Re: Smart Pointer Template Class by warjo
08-28-2001 - 04:20 AM
       Re: Smart Pointer Template Class by onno
08-28-2001 - 05:56 AM
       Re: Smart Pointer Template Class by Justin Wilder
08-28-2001 - 01:10 PM
              Re: Smart Pointer Template Class by warjo
08-29-2001 - 04:47 AM
                     Re: Smart Pointer Template Class by Altair
08-29-2001 - 05:09 AM
                            Re: Smart Pointer Template Class by warjo
08-29-2001 - 05:54 AM
                                   Re: Smart Pointer Template Class by Altair
08-29-2001 - 06:44 AM
                     Re: Smart Pointer Template Class by Justin Wilder
08-29-2001 - 03:08 PM
                            Re: Smart Pointer Template Class by Altair
08-30-2001 - 03:43 AM
                            Re: Smart Pointer Template Class by Sampsa Lehtonen
08-30-2001 - 06:19 AM
                                   Re: Smart Pointer Template Class by Justin Wilder
08-30-2001 - 02:50 PM
                            Re: Smart Pointer Template Class by warjo
08-30-2001 - 06:23 AM
                                   Re: Smart Pointer Template Class by Justin Wilder
08-30-2001 - 01:31 PM
                                          Re: Smart Pointer Template Class by warjo
08-31-2001 - 03:35 AM
 
 
 

  
 
I hope you don't mind constructive criticism because this is yet another small nit-pick, but you really shouldn't use



#ifndef __POINTER_H__



as your multiple header-inclusion guard identifier.

The C++ standard dictates that:
"Any name with two consecutive underscores in any position is reserved for the implementation."




ynohtna

Other Posts By This Author

08-28-2001 - 05:54 AM

post reply - up-thread - alert

 

  
 
If you really want to learn a *lot* about smart pointers and all sorts of other tricks in C++, I highly recommend the book Modern C++ Design [link]. The web page has the first chapter in PDF, and if that doesn't hook you, I don't know what would.



Chad Austin

Other Posts By This Author

08-28-2001 - 02:27 PM

post reply - up-thread - alert

 

  
 
One of my lacmus tests for whether someone understands C++ is to look at their implementation of a smart pointer. (If someone doesn't use a smart pointer he/she fails immediately.) Basically, it is very easy to make an unsafe smart pointer. (And even easier to write memory leaks and create unclear design if not using a smart pointer.)

Assuming that someone would submit a smart pointer that would address only all of the issues raised in this thread, I still would not give it full points for safety. It would still be lacking in at least the following aspects:
- implicit conversions are unsafe and should simply be removed
- no pre- and postconditions (dereference needs precondition)
- unsafe null testing (the current suggestions open safety holes)
- unsafe deletion (incomplete types)

Also, the flexibility of the smart pointer could still be improved considerably without adding template parameters to the smart pointer class. For instance, the smart pointer comparison functions should be symmetric global/friend functions.



Vesa Karvonen

Other Posts By This Author

08-29-2001 - 04:43 PM

post reply - up-thread - alert

 
 
 
    Re: Smart Pointer Template Class by Vesa Karvonen
08-29-2001 - 04:43 PM
       Re: Smart Pointer Template Class by Justin Wilder
08-29-2001 - 07:31 PM
       Re: Smart Pointer Template Class by Gladius
08-30-2001 - 06:09 AM
 
 
 

  
 
And why not to use shared_ptr from boost?


Sergey

Other Posts By This Author

08-30-2001 - 08:28 AM

post reply - up-thread - alert

 

  
 
this template file doesn't work together with the mmgr by paul nettle. any suggestions?



my demos and stuff can be found at http://www.daimi.au.dk/~kaae



Rasmus Christian Kaae

Other Posts By This Author

10-04-2001 - 05:47 AM

post reply - up-thread - alert

 

   Navigate: / flipcode / Message Center / Code Of The Day Forum /



( post a message to this thread )


User Name:


Password:
If you've never posted here, read the guidelines for formatting and rules. If you have not created an account yet, create one here!

Note: To post syntax highlighted c++ code, use the <cpp> :: code goes here :: </cpp> tag. HTML support is currently: enabled.
Subject:


Message Text:

   

Copyright ?1998-2001 FLIPCODE.COM, INC. All Rights Reserved. Please read our
Terms, Conditions, and Privacy information.
This site is optimized for at least 1024x768 resolution (hi-color) viewing with a browser that supports style sheets.