Reputation: 5363
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));
Upvotes: 1
Views: 1919
Reputation: 11732
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.
Upvotes: 0
Reputation: 1492
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.
Upvotes: 1
Reputation: 25522
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.
Upvotes: 2
Reputation: 5523
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.
Upvotes: 0
Reputation: 8318
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.
Upvotes: 2
Reputation: 170489
Why not introduce a common base with a virtual destructor, inherit all necessary classes from it and simply use new?
Upvotes: 1
Reputation: 4659
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.
Upvotes: 4
Reputation: 29345
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;
}
Upvotes: 1