views:

711

answers:

8

I'm working on C++ framework and would like to apply automatic memory management to a number of core classes. So far, I have the standard approach which is

class Foo 
{

public:

  static
  shared_ptr<Foo> init() 
  {
    return shared_ptr<Foo>(new Foo);
  }

  ~Foo() 
  {
  }

protected:

  Foo()
  {
  }

};


// Example of use
shared_ptr<Foo> f = Foo::init();

However, the above breaks when I subclass Foo, since even tho init() is inherited, it still returns shared_ptr<Foo> which contains a pointer to instance of Foo.

Can anyone think of an elegant solution to this? Should I perhaps just stick with (semi-)manually wrapping instances of class with shared_ptr? This would also give ability to expose parameterized constructors without declaring new named constructors...

Ie.

template <typename T>
shared_ptr<T> make_shared(T* ptr)
{
  return shared_ptr<T>(ptr)
}

// Example
shared_ptr<T> 
  f1 = make_shared(new Foo()),
  f2 = make_shared(new Foo(1,2));
+1  A: 

How about...

template<typename Derived>
class Foo 
{
public:

    static shared_ptr<Derived> init() 
    {
        return shared_ptr<Derived>(new Derived);
    }

    ~Foo() 
    {
    }

protected:

    Foo()
    {
    }

};


class Bar : public Foo<Bar>
{
};

int _tmain(int argc, _TCHAR* argv[])
{
    shared_ptr<Bar> b = Foo<Bar>::init(); 
    return 0;
}
SDX2000
+4  A: 

I would try something like this:

template<class T>
class creator
{
  public:
    static shared_ptr<T> init()
    {
      return(shared_ptr<T>(new T));
    }
};

class A : public creator<A>
{
};

class B : public A, public creator<B>
{
  public:
    using make_shared<B>::init;
};

// example use
shared_ptr<A> a = A::init();
shared_ptr<B> b = B::init();

But this isn't necessarily saving you a thing compared to standalone template you proposed.

Edit: I missed previous answer, this seems to be the same idea.

Tomek
+1  A: 

Why not introduce a common base with a virtual destructor, inherit all necessary classes from it and simply use new?

sharptooth
+3  A: 

I don't understand what this achieves, you don't appear to be getting any extra memory management using this init function than by simply declaring a shared_ptr.

int main( void )
{
    shared_ptr<foo> a = foo::init();
    shared_ptr<foo> b( new foo );
}

What's the difference. shared_ptr provides the memory management, not anything in init.

Patrick
If 'shared_ptr' has an explicit constructor, then your second example won't compile, since "foo*" does not implicitly convert to "shared_ptr<foo>". (This will work on VC but its due to a bug in VC not becuase it's legal.)
Richard Corden
Yeah, but this is an example. If you're worried about compiling how about there being no headers, no namespace for shared_ptr, no declaration of foo... On the other hand the concept is obvious.
Patrick
i agree with patrick. i've noticed it immediately but refrained from giving the commentary, because it's really just an example :) anyway, i think his implied question should be answered by the original poster. i'm curious.
Johannes Schaub - litb
Basically the idea was to disallow non-managed heap or stack creation. However, I suppose adding a "do this or die" comment to the documentation will also achieve that and spare me from all those tricks.
Dimitri Tcaciuc
Could be classed as premature optomisation. One day someone may have a very good reason to use a basic ptr to your objects. If you have to support their code after that day then you definetly have the right to kill them.
Patrick
Good point. All of the facilities which are using these objects are taking in `shared_ptr` anyway, so I guess that is a good enough hint for whoever is using them.
Dimitri Tcaciuc
Particularly when the perverse programmer can always use shared_ptr<foo>::get to get at the raw pointer and break the management scheme...
Sol
They -really- need to give jail time for stuff like that...
Dimitri Tcaciuc
A: 

You need the static factory function in every type of the entire hierarchy.

class Foo
{
public:
    static shared_ptr< Foo > instantiate( /* potential arguments */ )
    {
           return shared_ptr< Foo >( new Foo( /* potential arguments */ );
    }

// blah blah blah
};

class Bar : public Foo
{
public:
    static shared_ptr< Bar > instantiate( /* potential arguments */ )
    {
           return shared_ptr< Bar >( new Bar( /* potential arguments */ );
    }

// blah blah blah
};

If you still have any confusion, please search CppCodeProvider on sourceforge and see how its done there.

Tanveer Badar
+2  A: 

It seems that the goal is to make it impossible for users of the classes to call the constructors directly, and only expose a routine which returns shared_ptr's.

But if you want to apply this pattern, you need to replicate it in all the subclasses. The subclasses cannot automatically "inherit" init() so that init() would still call the subclass constructor, because init() is not a virtual method and is called without an object.

I would leave the constructors exposed as usual and just use the standard

shared_ptr<X> x = new X();

This keeps cognitive burden low, is readable, and remains flexible. This is how we program in our company with reference counted objects, anyway.

antti.huima
+1  A: 

It's generally not a good idea to force creation of objects using shared_ptr by hiding the constructors. I'm speaking from personal experience here working with an internal company lib that did exactly that. If you want to ensure people always wrap their allocated objects, just make sure that all arguments and members which store instances of these types expect a shared_ptr or weak_ptr instead of a naked pointer or reference. You might also want to derive these classes from enable_shared_from_this, because in a system where all objects are shared, at some point you'll have to pass the this pointer to one of these other objects' methods, and since they're designed only to accept shared_ptr, you're in pretty bad shape if your object has no internal_weak_this to ensure it isn't destroyed.

Michel
You know, I never understood how that thing works. In source code they have no assignment to _internal_weak_this and explicit comment "no, you don't have to assign anything to it". What gives?
Dimitri Tcaciuc
Look at the shared_ptr.hpp source--specifically the function sp_enable_shared_from_this that is called from the ctor(s) and the nice little trick using the sp_any_pointer dummy.
Michel
A: 

By the way, in large C++ frameworks it's common to hide the "automatic memory management" from the coder. This lets him write shorter and simpler code. For example, in Qt you can do this:

QPixmap foo() {
    QPixmap pixmap(10, 10);
    return pixmap;
}

void bar() {
    QPixmap a = foo(); // no copying occurs, internal refcount incremented.
    QPixmap b = a;     // ditto.
    QPainter p(&b);
    p.drawPoint(5, 5); // data can no longer be shared, so a copy is made.
    // at this point 'a' is still unchanged!
    p.end();
}

Like many things in Qt, this mimics the Java object model, but it goes further by implementing copy-on-write (which it calls implicit sharing). This is intended to make the API behavior less suprising to C++ coders, who aren't used to having to call clone().

This is implemented via the d-pointer idiom, which kills two birds with one stone - you provide automatic memory management, and you insulate your implementation from the user (pimpl).

You can look at the actual implementation of QPixmap here: qpixmap.cpp, qpixmap.h.

Stefan Monov