Click here to Skip to main content
15,885,944 members
Articles / Programming Languages / Visual C++ 10.0

Object Creation using Smart Pointers and Map

Rate me:
Please Sign up or sign in to vote.
4.50/5 (2 votes)
6 Dec 2012CPOL5 min read 17.6K   87   4  
Using SmartMap to create objects that will clean up after themselves
/*-----------------------------------------------------------------------------------------------------------------------
// By: Kenneth MacLean 2012

//Usage
SmartMap<string,Foo> fooMaker;
Observer<Foo> fooObserver;
{
   Retain<Foo> foo1 = fooMaker["id 001"]; // creates a Foo with key "id 001"
   Retain<Foo> foo2 = fooMaker["id 001"]; // doesn't need to create a Foo as it already exists
   Retain<Foo> foo3 = foo2;               // now we have 3 references to the same Foo

   cout << foo1.get()->name << " has " << foo1.use_count() << " references" << endl;
   // foo1, foo2, foo3 all have a reference count of 3 as they all contain the same Foo
   // output: "id 001 has 3 references"

   fooObserver = foo1;
   cout << fooObserver._Get()->name << " has " << fooObserver.use_count() << " references" << endl;
   // notice that we still have 3 references even though we've added an observer
   // that's because the observer has no baring on the reference count
   // output: "id 001 has 3 references"

   {
      Retain<Foo> foobar = fooMaker["id 999"];
      fooObserver = foobar;
      cout << fooObserver._Get()->name << " has " << fooObserver.use_count() << " references" << endl;
      // the foo observer is now observing a foobar and reports that it has only 1 reference
      // output: "id 999 has 1 references"

      foobar = foo1;
      cout << "fooObserver expired? " << std::boolalpha << " and has " << fooObserver.use_count() << " references" << endl;
      // fooObserver is still pointing to a Foo with the key "id 999" but foobar is 
      // now pointing to "id 001", so the Foo with the key "id 999" has gone out of scope and 
      // deleted itself (expired). Remember, just because fooObserver is still pointing to 
      // it doesn't mean it is still in scope, because observers have no baring on the reference count
      // output: "fooObserver expired? yes and has 0 references"

      fooObserver = foo1;
      cout << fooObserver._Get()->name << " has " << fooObserver.use_count() << " references" << endl;
      // since foobar has been added to the Foo with the key "id 001" we now have a reference count of 4
      // output: "id 001 has 4 references"
   }


   fooObserver = foo1;
   cout << fooObserver._Get()->name << " has " << fooObserver.use_count() << " references" << endl;
   // foobar is now out of scope so the Foo with the key "id 001" has dropped 1 reference
   // output: "id 001 has 3 references"
}

cout << "fooObserver expired? " << std::boolalpha << fooObserver.expired() << " and has " << fooObserver.use_count() << " references" << endl;
// all the foos have gone out of scope so the Foo with the key of "id 001" has been deleted
// output: "fooObserver expired? yes and has 0 references" 

------------------------------------------------------------------------------------------------------------------------*/

#pragma once

#include <map>
#include <memory>

#define Retain std::shared_ptr
#define Observer std::weak_ptr

template < typename Key, typename T >
class SmartMap
{
protected:

	// these are called observers because they are automatically deleted when
	// the requester has finished with it (goes out of scope). 
	// Each object must have a unique key as 2 references with the 
	// same key point to the same object
	std::map<Key,Observer<T> > observers;
	
public:

	// example: below, foo and sameFoo are pointing to the same obj because they share the same key
	// Retain<Foo> foo = fooMap["id 0001"];          // creates a Foo with key "id 0001"
	// Retain<Foo> sameFoo = fooMap["id 0001"];      // doesn't need to create a Foo as it already exists
	// Retain<Foo> differentFoo = fooMap["id 0002"]); // creates a Foo with key "id 0002"
	Retain<T> operator[](Key key)
	{
		// First it checks to see if an observer has already been created
		// using the key, remember that the same key will return the same object.
		// If it maps to an observer and it has expired, this means
		// that all references that belong to this key have gone out of scope,
		// so a new object will have to be created. If an Observer<T> has been found 
		// then we can create a Retain<T> from it by calling lock()
		auto it = observers.find(key);
		if(it != observers.end() && !(*it).second.expired())			
			return (*it).second.lock();
		
		Retain<T> newT = Retain<T>(new T(key));
		
		observers[key] = newT;

		return newT;
	}

	// This may need to be called if the map has a lot of expired entries
	void flushExpired()
	{
		for(auto it = observers.begin(); it!= observers.end(); )
		{
			if((*it).second.expired())
				observers.erase(it++);
			else
				it++;
			
		}
	}
};

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Laservision
Australia Australia
I have been programming since I was a kid, text based adventure games on the C64 in those good old days then went on to complete CS degree. Since then I've been a contract programmer for the last 12 years developing for web, mobile, desktop and game consoles. Currently I'm working for Laservision were I get to play with lasers and video projectors for fun and profit.

So glad C++ is going through a bit of a renaissance at the moment as it's my favourite language

Comments and Discussions