Skip to main content
GameDev.net gamedev.net
🔒 Locked

Polymorphic Smart Pointers

Started by Cornstalks Jun 16, 2008 at 11:13 AM 9 replies 2.2k views
Original Post
Cornstalks
Cornstalks
Is there a way that I can use polymorphism with smart pointers? More specifically, I have a std::stack of boost::shared_ptr. But when I tried stack.push(new Derived), the compiler gave me the following error:
no matching function for call to `std::stack<boost::shared_ptr<Base>, std::deque<boost::shared_ptr<Base>, std::allocator<boost::shared_ptr<Base> > > >::push(Derived*)' 
note C:\MinGW\include\c++\3.4.5\bits\stl_stack.h:191 candidates are: void std::stack<_Tp, _Sequence>::push(const typename _Sequence::value_type&) [with _Tp = boost::shared_ptr<Base>, _Sequence = std::deque<boost::shared_ptr<Base>, std::allocator<boost::shared_ptr<Base> > >]
Is there a way to do this?
bubu LV
bubu LV
Try
stack.push( boost::shared_ptr<Base>( new Derived ) );
vtchill
vtchill
Just to add to the above post, it is potentially dangerous to use an unnamed temporary shared_ptr as it has the potential to cause a memory leak.

Something like this would be a better alternative.

boost::shared_ptr<Base> obj(new Derived(/*constructor arguments here*/));stack.push(obj);
fpsgamer
fpsgamer
Quote:
Original post by vtchill
Just to add to the above post, it is potentially dangerous to use an unnamed temporary shared_ptr as it has the potential to cause a memory leak.

Something like this would be a better alternative.

*** Source Snippet Removed ***


Actually it isn't a problem in that case. It would only be a problem if the function accepted multiple arguments.

reference
Cornstalks
Cornstalks
Sweet, thanks to all! I'll try it out. I'm getting to the point where I'm using more and more polymorphic objects and I don't want to worry about memory leaks, so I figured I'd try to use some smart pointers for some extra safety.

[edit]

Works perfectly!
Red Ant
Red Ant
To get back to the original post, a boost::shared_ptr can definitely be set to point to a Derived. I don't know why the compiler is giving you an error there but this really *should* work IMO.


P.S. Ah never mind ... I should learn to read posts before writing replies.
SiCrane
SiCrane
Quote:
Original post by Red Ant
I don't know why the compiler is giving you an error there but this really *should* work IMO.


It doesn't because the shared_ptr constructor that takes a single pointer is explicit.
Red Ant
Red Ant
Yeah sorry ... I hadn't read the post properly before replying. I see now that he was relying on an implicit conversion.
chairthrower
chairthrower
Quote:
Original post by fpsgamer
Quote:
Original post by vtchill
Just to add to the above post, it is potentially dangerous to use an unnamed temporary shared_ptr as it has the potential to cause a memory leak.

Something like this would be a better alternative.

*** Source Snippet Removed ***


Actually it isn't a problem in that case. It would only be a problem if the function accepted multiple arguments.

reference


youch ... i never considered this. my code has a lot of this kind of thing, (the justification for the init() seperate from the constructor is for dealing with things that interact with shared_from_this())

class A{     typedef shared_ptr< A>  ptr;      static ptr create()      {         ptr p( new A);          p->init();           return p;      }};


is it safe to use in this type of context.

vector< A::ptr>    va; va.push_back( A::create() );


i think its ok - its still a temporary being returned that is used as the const arg of the push_back method, but both the shared pointer and underlying object have been fully constructed and use_count() is always positive even if its only being held by a temporary. what about multiple argument functions . if one of the other arguments methods throws on the constructor - would that still be memory safe ?
fpsgamer
fpsgamer
Quote:
Original post by chairthrower
i think its ok - its still a temporary being returned that is used as the const arg of the push_back method, but both the shared pointer and underlying object have been fully constructed and use_count() is always positive even if its only being held by a temporary. what about multiple argument functions . if one of the other arguments methods throws on the constructor - would that still be memory safe ?


The scenario you illustrated is safe.

The problem (as you've already noted) has to do with when you are dealing with multiple arguments.

Consider this:

void doStuff(boost::shared_ptr(new Foo()), Bar() );

The order in which arguments are evaluated in C++ is undefined. If an exception is thrown at inopportune times you will get a resource leak. Consider this hypothetical argument evaluation order:


(1) exectute 'new Foo()'
(2) construct temporary Bar() object
(3) construct shared_ptr from the value returned by 'new Foo()'

If (2) two throws an exception, stack unwinding will commence and we will loose a handle to the memory allocated in (1).

Thats why you should do this instead:

boost::shared_ptrptr(new Foo());
void doStuff(ptr, Bar() );


If stack unwinding begins as the result of a thrown exception, the shared_ptr will be destroyed and take appropriate care of the heap resource.

Further Reading.
chairthrower
chairthrower
Thanks very much for the assurances!! and explanation. my body jerked upright when i read these posts - though i now see it discussed if a bit off-handedly in the boost documention. i will make sure to go through both of your references so i understand all implications in the morning when my mind is fresh. thanks again.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.