views:

63

answers:

3

If I use a container of shared_ptrs and explicitely allow access to its elements, should I return shared_ptrs or raw pointers if I intend the container to be the one responsible for "cleaning up"?

class Container
{
private:
    std:vector<shared_ptr<Foo> > foo_ptrs;

public:
    shared_ptr<Foo> operator[](std::size_t index) const {}; // or
    Foo* operator[](std::size_t index) const {};
};

Is there a reason to return shared_ptrs in such a situation, or are raw pointers OK?

Greets!

+2  A: 

You should return a shared_ptr if you want to continue to use the object without worrying about the container element being erased.

Otherwise, you can wind up with an orphaned raw pointer, after some other container user either cleans up the container or erases your referenced element.

Steve Townsend
Even if the container exists at least as long as anything else that would use the pointers (and no deletion is possible)?
jena
@jena - in that case you should be fine to use raw pointers. Assumptions have a way of changing over time though.
Steve Townsend
+2  A: 

return a reference

Only return a shared_ptr if you intend the accessors to share in the lifetime management. That is a valid design but as you said Container is solely responsible for the cleanup. shared_ptr is just an implementation detail of Containter, that fact that there is a vector of shared_ptrs used to implement the container shouldn't be exposed through the interface.

Don't return a pointer unless it make sense to get a NULL from Container. Usually it does not. All the user wants is access to the i-th element of the container and a reference does that perfectly.

What you really want is a std::vector<std::unique_ptr<Foo>>. Container is the one managing the memory and says so in member declaration. Everyone else has no business knowing the implementation.

Take a look at Boost.Pointainers if you're unwilling or unable to use C++0x.

caspin
Thanks for the clear explanation, boost's pointer containers look good. There is one point I missed (myself), though: References won't work in cases where types need to be stored in containers of polymorphic base classes... Guess that would have to be handled differently.
jena
@jena: why would references not work in that case? A reference-to-base can be bound to a derived object, exactly the same as a pointer-to-base can point to a derived object. It's the same polymorphism.
Steve Jessop
If I want to store different types of derived classes in one and the same container, I have to store pointers of the base class, don't I?
jena
@jena: yes you'll need to store the polymorphic types by pointer in the container. However, the interface should only consist of references, ie `T }`
caspin
@caspin: That sounds reasonable. Consequently, if I want to provide iterator access to the underlying container of pointers, I would have to write an iterator that returns references instead pointers, right?
jena
@jena: yes, that's right. It's exactly what the Boost pointer containers do.
Steve Jessop
+1  A: 

What you probably "should" return in this case is a boost::weak_ptr<>, which give you the semantics you are describing (where the container maintains ownership, the object can still be deleted after the external reference is given out, and you can safely discover if the object is still valid on external usage). Of course that requires using boost::shared_ptr<> I think, so it may not be applicable for your situation.

Hope that helps, even if it may not be feasible for you to use.

Nick