views:

289

answers:

6

So, I use boost::shared_ptr for all the various reference-counting benefits it provides -- reference counting for starters, obviously, but also the ability to copy, assign, and therefore store in STL Containers.

The problem is, if I pass it to just one "malicious" function or object, the object can save the ptr and then I'll never be able to de-allocate it without the foreign function or object nicely relinquishing its ownership.

Ultimately, I try to keep object ownership explicit. I accomplish this by having the owner keep the only shared_ptr to the object, and "guest" objects only store weak_ptrs to the object.

I really don't want the "shared" part of shared_ptr, but I'm required to use shared_ptr in order to make weak_ptrs. I want to use scoped_ptr, but it's extremely limited since you can't copy it. You can't store it in a container, you can't lend out weak_ptrs from it, and you can't transfer ownership to a new manager.

What's the solution?

+8  A: 

Make it private and provide a facade to do whatever operations needed. Nobody ever sees the pointer. I guess that at that point you would not even need a shared_ptr.

Romain Hippeau
+1 for private, though the obvious benefit of `shared_ptr` here is the ability to get `weak_ptr`, which allow the user to know whether or not the original object is still alive.
Matthieu M.
+1  A: 

It's good enough to use weak_ptr for guest objects, as you described in question. Otherwise you will have a problem with dead pointers.

I would consider to do application rearchitect to remove "malicious" functions/objects or at least fix their behavior.

Eugene
As it turns out, I control all of the code so I wound up removing the "malicious" (or should I say "naive") code myself. I'm going to keep using shared_ptrs and just be more careful by using weak_ptr whenever I possibly can, because I do need the expired() check. I really hate doing it this way, because quite often I feel like I'm my own worst enemy because I keep accidentally saving shared_ptrs when I meant to save a weak_ptr. It seems like this is a big problem with shared_ptrs in general though, because they're often stickyer than you want them to be.
Kyle
Accepting this answer because it's what I personally wound up doing, but it wouldn't help anyone that has limited control over the code. Thanks everyone for posting their solutions, they all look good to me. I wish boost would come out with 'owned_ptr' or something...
Kyle
A: 

You could extend the shared_ptr boost class and override delete to force delete the pointer.

The issue really is that, if the library is not releasing or freeing the shared_ptr then its likely to refer to it some time. At this time your application will fall with a SIGSEGV.

I think it totally invalidates the purpose of shared pointers.

The best solution is to fix the library.

Other solution, use AOP to delete pointer on exit of the library function you are calling. This is still likely to break.

arunmur
+4  A: 

Don't pass around the boost::shared_ptr object... even if you store the object, internally, using a boost::shared_ptr, you should make sure that functions take your object by constant reference rather than a copy of the shared pointer. Since you would need to dereference the shared pointer in order to pass the object to a function that passes by const reference, you will know whether it follows that protocol or not.

Michael Aaron Safyan
A few rules of thumb for passing the objects by pointer or reference:a) Pass by plain/auto pointer only if you want to transfer ownership (basically shared_ptr constructor only)b) If the callee has to retain the access no matter what, pass by shared_ptr CONST reference or value (passing by shared_ptr non-const reference has very obscure semantics, if you start to think about it - use it only when really needed). You can assure this on caller side by casting your shared_ptr to const.c) In all the other cases, pass by (const) reference to the object. You can assure this when calling, too.
Mart Oruaas
A: 

There's really no good solution for what you're describing.

You can't use auto_ptr because you're not transferring ownership.

If you can guarantee the owner outlives the references, I'd recommend using a scoped_ptr/store by value in the owner and then passing a raw pointer or reference to those that need it.

If references can outlive the owner (and the references need to be gracefully notified), you have to use shared_ptr/weak_ptr. But, as you stated, you cannot prevent any class/function from locking the weak_ptr and "preventing" deallocation. But, in the interface, do not pass the shared_ptr, pass the weak_ptr. It's only as strong as a convention, but it says "don't hold onto this, it may go away".

Nathan Ernst
A: 

If you want to handle your objects in that owner/owned paradigm I recommend doing something like Qt.

  1. Create a base class Object, which all classes in your system will inherit from. Each object keeps track of its parent/owner and children.
  2. Use a setOwner(Object * owner) method to set the owner and have that method make sure that the owner object is notified about the new child.
  3. The destructor for Object should delete all of the child objects when it is destroyed.
  4. Define a template class which defines a smart pointer to an Object subclass. Make it so that the Object notifies any connected smart pointers about its destruction so that there values will become NULL when the object is destroyed.

Things to watch out for:

  1. All objects must be allocated via new for Objects destructor to correctly deallocate them.
  2. If you forget to set the parent for an object, it'll leak
  3. Handling objects which don't inherit from Object is tricky, although it can be done by defining a template class which does inherit from Object to hold them.
Winston Ewert