views:

230

answers:

2

Hello stack overflowers, I have a desing that uses the flyweight pattern to share bitmaps that are shared between bitmap objects which manage drawing ops, etc. and integrate in the gui library. This is an embedded device so memory is at a premium. Currently I have done a working implementation with a std::vector of auto_ptr of a light class that counts usage. I know this is a bad idea and may leak, so I'm rewriting this part. I'm considering using boost::shared_ptr. The key of my question is that I'd like the bitmaps to be released if there are not being used. If I have a pool of shared_ptr I end up with the used bitmaps loaded once. I'm considering using shared_ptr::use_count() to delete the bitmap if use_count() == 1. But the docs warn against production code of use_count(). Basically the question is flyweight pattern with releasing of individual heavy objects. Do you think there's a better way to do this?

A: 

The idea of shared_ptr is that it manages the object lifetime for you. You shouldn't have to check the use count. Have one shared_ptr per user, remove the shared_ptrs when you remove users, and when there are no more users left, it will delete the bitmap for you. If you have a special routine that you need to call to delete the bitmap and can't just do that in the destructor, you can pass a special deleter function to the shared_ptr on creation.

Kylotan
You miss the point that there's a pool of heavy objects, it won't get deleted after use if a shared_ptr remains alive.
piotr
The collection of shared pointers can effectively be the pool. This is exactly the sort of thing they are designed for, to be references to large objects that are shared and to delete an object when nothing is left referring to it.
Kylotan
+5  A: 

You could use a pool of boost weak pointers so that the pool does not count in the ownership.

Only the bitmap objects have boost shared pointers, this way they decide when to release the bitmaps.

The pool of weak pointers allows us to retrieve the already constructed bitmaps :

When you create a bitmap object you either :

  • get a shared pointer from the weak pointer if it is not empty,

  • or otherwise load the new bitmap, make a new shared pointer from it and insert/replace the weak pointer in the pool.

Here is some sample code using a map for the pool :

#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <map>
#include <string>
#include <iostream>

// represents the bitmap data
class Bitmap
{
public :
    Bitmap( std::string const& name ) : name( name )
    {
     std::cout << "Bitmap " << name << std::endl ;
    }

    ~Bitmap()
    {
     std::cout << "~Bitmap " << name << std::endl ;
    }

    std::string name ;
};

// the flyweight pool
class Factory
{
public :

    typedef std::map< std::string , boost::weak_ptr< Bitmap > > Map ;

    boost::shared_ptr< Bitmap > get( std::string const& what )
    {
     Map::iterator x = map.find( what );

        // retrieve existing object from map's weak pointers

     if( x != map.end() )
     {
      if( boost::shared_ptr< Bitmap > shared = x->second.lock() )
      {
       return shared ;
      }
     }

        // populate or update the map

     boost::shared_ptr< Bitmap > shared( new Bitmap( what ) );
     boost::weak_ptr< Bitmap > weak( shared );
     map.insert( std::make_pair( what , weak ) );
     return shared ;
    }

private :
    Map map ;
};


int main(int argc, char** argv)
{
    Factory f ;

    // we try our flyweight bitmap factory ...

    boost::shared_ptr< Bitmap > a = f.get( "a" );
    boost::shared_ptr< Bitmap > b = f.get( "b" );

    // a is not made again
    boost::shared_ptr< Bitmap > a2 = f.get( "a" );

    a.reset();
    a2.reset();

    // a is destroyed before ------

    std::cout << "------" << std::endl ;
}
fa.
I like it. But I think a bit more description would be nice.
Martin York
I think it's a very good idea! I hadn't thought of using weak_ptrs that way. I have it currently working with use_count() and seems it's not leaking. I'll give a try to the weak_ptr solution to see if it works indeed.
piotr
ok, I tried to make it clearer
fa.
There was no need for the code, but it's quite concise and good. Thanks.
piotr