Class factories and shared_ptr<T>

One way to control object memory and initialization is to wrap object creation in a class factory. This is the point of the proposed make_shared<T>(..) and allocate_shared<T>(..) standard library functions.

But it is easy to roll your own simple factory function:

# include <boost/shared_ptr.hpp>
using boost::shared_ptr;

  class
factory_type
{
    /* this_type type alias */
    private:
    typedef
    factory_type
  this_type;

  // ======== Disable Copy ============

    /* disabled copy ctor */
    private:
  factory_type( this_type const &)
    ; /* no implementation */

    /* disabled copy assignment */
    private:
    void
  operator =( this_type const &)
    ; /* no implementation */

  // ======== Factory =================

    /* private default ctor */
    private:
  factory_type( )
    { }

    /* public (unfortunately) dtor */
    public:
  ~factory_type( )
    { }

    /* factory, only way to make these objects */
    public:
    static
    shared_ptr< this_type >
  make_new( )
    { return
        shared_ptr< this_type >(
          new this_type);
    }
};

  shared_ptr< factory_type >
factory_inst(
  factory_type::make_new( ));

And there you have factor_type, a tightly controlled class where all insts are created in the factory (factory_type::make_new( )) and all deletes are through shared_ptr<T>s. The private constructors means not only can we not “new” the class ourselves, we also cannot use it as a supertype. Nor can we declare a factory_type class member or stack variable.

factor_type would be improved, however, if we could ensure that only shared_ptr<T>s could access the destructor. It doesn’t work to declare the destructor private and friend class shared_ptr< this_type > because the destructor is actually called from a deleter function. However we can friend the deleter function:

  class
factory_type
{
  ... as above ...

    /* private dtor */
    private:
  ~factory_type( )
    { }

    /* the only function that calls the dtor */
    friend void boost::checked_delete( this_type *);

    /* factory, only way to make these objects */
    public:
    static
    shared_ptr< this_type >
  make_new( )
    { return
        shared_ptr< this_type >(
          new this_type);
    }
};

This works fine, but you get the following warning from msvc++9.0: the inline specifier cannot be used when a friend declaration refers to a specialization of a function template. Of course you can disable this warning. It’s not very important and likely to go away in a future version of msvc++ anyway, and it may even be corrected by an optimized non-debug compile.

The nicest solution, however, is to provide a private non-templated deleter. It doesn’t issue the above warning and it feels more portable. It’s only drawback is that it slightly increases the size of the intermediate ref-count object.

  class
factory_type
{
  ... as above ...

    /* private dtor */
    private:
  ~factory_type( )
    { }

    /* the only place where the dtor is used */
    private:
    struct
  private_deleter
    {   void
      operator ()( this_type * p)
        { delete p; }
    };

    /* factory, only way to make these objects */
    public:
    static
    shared_ptr< this_type >
  make_new( )
    { return
        shared_ptr< this_type >(
          new this_type,
          private_deleter( ));
    }
};

Comments

Leave a Reply