Sign in to follow this  
Trenki

boost::shared_ptr: When to use it and when NOT to use it.

Recommended Posts

Trenki    345
Hi! I find boost::shared_ptr very handy for liftetime management of objects and I feel like as if it would be a good idea to use it nearly everywhere to avoid having to deal with manual lifetime management (beware of cycles). But somethings sounds wrong to me in this idea. So, is there a good advide that says when and when NOT to use shared pointers or would it be a good idea to use them pratically everywhere? What are the downsides of using shared pointers, is the overhead large enough to be problematic? On the contrary, what are the downsides of not using shared pointers?

Share this post


Link to post
Share on other sites
Telastyn    3777
It's maybe a bad idea in cases where you're mostly going to end up just passing the raw pointer into a library or API that requires raw pointers. But for the majority of things you will probably want some form of smart pointer in modern C++ programming.

Share this post


Link to post
Share on other sites
For most cases, the answer should probably be: "always", since smart pointers (obviously) avoid a lot of pain.

However, it has to be said that the particular example of boost::shared_ptr may not be the right thing to use if there are many assignments/copy operations in a place where performance is paramount.
boost::shared_ptr does all of its reference counting atomically (library calls on some platforms, locked atomic instructions on others). While that's a good thing regarding thread safety, it can be a performance killer if used uncritically everywhere.
The overhead should be neglegible under normal use, but one should probably avoid passing around a pointer several times in a loop that runs a few thousand times per frame, or putting 10k smart pointers into a std::vector and resize it afterwards (hey, don't ask me why I would want to do that...).

Share this post


Link to post
Share on other sites
Sneftel    1788
You should certainly use shared_ptr by default. However, definitely think twice in areas where that would involve a large number of pointer operations per frame. In particular, particle systems and geometry meshes should prefer to use raw pointers internally. Other than those situations, I've never seen shared_ptr near the top of my profiling report.

One thing to keep in mind: Whenever possible (which is nearly always), pass shared_ptrs as const references. This avoids incrementing, decrementing, and testing the use count.

Share this post


Link to post
Share on other sites
Omid Ghavami    1007
Quote:
Original post by epsylon
An obvious answer is not to use it when you should use scoped_ptr instead, that is when you do not want your pointer to be copyable.


Indeed, I also use weak_ptr very often.

Share this post


Link to post
Share on other sites
the_edd    2109
Quote:
Original post by Trenki
So, is there a good advide that says when and when NOT to use shared pointers


When you wanted value semantics rather than reference semantics, particularly when pointing to polymorphic objects (there are far worse sins than using shared_ptr in this situation, though!).

*cough* value_ptr *cough* :)

Share this post


Link to post
Share on other sites
NotAYakk    876
When you have a resource that must go away promptly at a particular time.

When you want reference semantics (or value semantics, as noted).

When you actually need a garbage collected pointer.

Share this post


Link to post
Share on other sites
the_edd    2109
Quote:
Original post by NotAYakk
When you have a resource that must go away promptly at a particular time.

When you want reference semantics (or value semantics, as noted).

When you actually need a garbage collected pointer.


These seem like situations where shared_ptr is applicable (depending on one's definition of garbage collection), rather than the opposite, as the O.P wants?

Share this post


Link to post
Share on other sites
NotAYakk    876
No?

shared_ptr means that any object with a shared_ptr reference to the thing in question can extend it's lifetime.

shared_ptr uses pointer semantics on assign. Ie, a = b means that a points to what b pointed to, and a constructed from b means a refers to the same thing as b.

Reference semantics a=b means what a points to is given the value of what b points to, and a constructed from b means that a refers to the same thing as b.

Value semantics a=b means that the value of a changes to be the value of b, and a constructed from b means that the value of a becomes the same as the value of b.

pointer semantics != reference semantics != value semantics.

shared_ptr implements pointer semantics.

Lastly, shared_ptr is a reference counted pointer, not a garbage collected pointer. A loop of shared_ptrs will persist forever, even if they aren't reachable by any code.

But sure, they make great defaults!

Share this post


Link to post
Share on other sites
the_edd    2109
Quote:
Original post by NotAYakk
No?

shared_ptr means that any object with a shared_ptr reference to the thing in question can extend it's lifetime.


Ah, I'm just looking at things a different way; a shared_ptr will keep the thing alive until the last bit of code that uses it has dissappeared. So while you would say that code is keeping it around, I would say that shared_ptr ensures that the life of a resource is as short as it safely can be (if used carefully).

Quote:
shared_ptr uses pointer semantics on assign. Ie, a = b means that a points to what b pointed to, and a constructed from b means a refers to the same thing as b.

Reference semantics a=b means what a points to is given the value of what b points to, and a constructed from b means that a refers to the same thing as b.


I tend not to distinguish between pointer and reference semantics and lump them under the same "referring to the same underlying object once constructed" heading.
as the value of b.

Quote:
pointer semantics != reference semantics != value semantics.


It's a language difference. I agree with what you are saying, I'm just not as rigorous with my definitions as I might be.

Quote:
Lastly, shared_ptr is a reference counted pointer, not a garbage collected pointer.


Some people would classify reference counting as a naive kind of garbage collection (hence the "depending on one's definition of garbage collection" part); if the definition of garbage collection is to release a resource when nothing is referring to it any longer, reference counting would fall under it (in cycle-free situations).

Yeah, so I'm in agreement. I will go away now :)

Share this post


Link to post
Share on other sites
Valere    248
The situation when a single point in my application must control the release of a given resource is when I use shared_ptr internally and give out weak_ptr's to other objects that must reference the resource.

This prevents references to deleted memory, and all of the bizarre errors that that can lead to, and has the upside of being easier to debug. It's not a panacea, but it converts a class of really nasty errors, to something more manageable.



Share this post


Link to post
Share on other sites
chairthrower    440
Avoid shared_ptr's in the binding of callbacks/events from sources/producers since they will keep the observer/consumer/sink object alive when its the only reference having a hold on the object. my approach is to bind a weak_ptr<> and test whether its alive before either calling or erasing the partially bound method from the event source site. An alternative approach would be to use a sort of reverse notification and employ an event which gets sent back to to the event producer to tell it to unbind the observers bound method when the observer is destroyed. its possible to place these in the custom Deleter of the shared_ptr that holds the observer but it feels a bit unnatural and over-engineered.

Also I think shared_ptr's can almost imply structuring an applications object graph in a tree structure otherwise you run a high risk of circular dependencies - possibly a very judicious use of weak references could be used to mitigate but the chances of something going wrong is high and it feels a bit like a workaround hack to me. there's a bit of an analogy here with the MS-office object models that use reference counted COM components and the manner in which they are structured in a tree like pattern as a means to avoid cyclic graphs/dependencies.

otherwise shared_ptr's are the best thing to happen to c++. One case where its possible to consider extending the use of shared pointer application is for resources that are not memory. You are permitted to pass a custom resource cleanup function to the shared pointer in place of the default delete. In effect you can define the policy for clean-up at the point of assignment.

An example from some db wrapper code of mine where the c api cleanup function PQfinish() for a database connection is bound as the custom deleter of the shared pointer.

/* Make a connection to the database */
PGconn *conn1 = PQconnectdb(conninfo.c_str());
/* bind clean up function */
shared_ptr< PGconn> conn( conn1, bind( &PQfinish, _1));

This feels cleaner to me than holding the raw pointer in an enclosing class and then remembering to call PQfinish() in that classes destructor to guarantee the connection wont leak if an exception is called.




Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this