0

Only just started looking into smart pointers and how they can be used to improve efficiency within code and I'm curious as to how shared_ptr reacts with class objects within an array/vector/container.

From what i've read the general gist of shared pointers is that once all of them pointing to an object have been destroyed or reallocated, the object is also destroyed. However if our object is also stored within an array/vector, does the shared pointer still attempt to destroy the object even though it is still accessible.

Is there some way to set up shared pointers with arrays in mind so you can ensure that you only delete the objects once you go to clear your array/vector, or would you have to resort to using shared_ptr in your array as well?

10
  • 2
    You should give an example of what you mean for it to be "in an array/vector". But yes, if all the shared_ptrs go, it gets destroyed. Commented Jul 23, 2014 at 20:37
  • 3
    Yes there is, use an array or vector of shared_ptrs. Commented Jul 23, 2014 at 20:37
  • 2
    There is no way for shared_ptr to know that you have a copy lying around in an array. Commented Jul 23, 2014 at 20:38
  • 1
    Shared pointers are all about ownership of the object. If an object is part of another object, then it is owned by that other object. It doesn't make sense to have a second entity that tries to own it too. Commented Jul 23, 2014 at 20:51
  • 1
    @Incubbus yes and no. A weak_ptr won't increment the reference count, but it's also designed to allow a graceful fail if the object it points to gets deallocated. It doesn't prevent the object from being deallocated, so you still have to check that it's valid before using it. Commented Jul 23, 2014 at 21:21

3 Answers 3

3

Mixing naked references with a shared_ptr is to be discouraged. The main reason is because it breaks the reference count concept, which is what the shared_ptr uses to determine when it is safe to deallocate the referenced object. Any naked references must have a shorter lifetime than the longest-living shared_ptr, otherwise a crash is brewing.

If you want to contain a set of objects in a container of some sort, and still be able to access those objects using a shared_ptr, it's best to use an array/vector/container of shared_ptr's, as this brings the container "into the fold" so to speak with the shared_ptr concept. This has the benefit that, when the object is removed from the container, it will not be immediately deallocated, and vice-versa with the shared pointers referencing it.

The downside of using a container of shared_ptrs is that it makes pointer math slightly more difficult since you are, in effect, working with a kind of pointer to a pointer.

Sign up to request clarification or add additional context in comments.

3 Comments

So are you passing around shared_ptr copies or references? How does it do reference counting if there are many shared_ptr objects?
Thanks, this is the kind of question I was looking for, been trying to look at how I can implement smart pointers to prevent issues with say exceptions that could lead to resource leaks and in a way have got a bit carried away with them before I've got a full grasp on them
@snowandotherjoys The implementation varies, but often shared_ptr objects use a static object to keep track of reference counts between instances.
2

You are expected to use shared pointers in only two ways:

1) When you create a new object,

std::shared_ptr<something> ptr(new something);

2) When you copy a shared pointer to another.

std::shared_ptr<something> copy(ptr);

So, if you create an object in an array, it won't fit the shared pointer scheme.

If you want to save an object allocated using a shared pointer in a vector, then you've got to save its shared pointer:

std::vector<std::shared_ptr<something> > my_vector;

my_vector.push_back(new something);

When you clear your vector, all the pointers get cleared and thus objects that were only referenced by the vector get deleted:

my_vector.clear();  // do "delete something" as required

As a side note, you say "more efficient" in your question... Shared pointers are not more efficient and they are not unlikely to create more code in your software than you would otherwise think of.

One very important point in C++ are exceptions. Objects saved in smart pointers (unique_ptr works too in that case) will automatically get deleted on exceptions. This is very important. For example:

std::shared_ptr<something> ptr(new something);

...
if(this_is_true) throw std::logic_error("something's wrong");
...

In the code above, the pointer ptr automatically gets deleted before the throw returns. This is not what I would call more efficient, but much better in terms of cleanliness. If you call a function that can throw (i.e. another new for example) then it becomes very tedious to handle each call (i.e. you'd need to have a try/catch around every single call and the catch would have to delete the pointers and then rethrow.)

So in that sense, it is efficient. However, in terms of speed of execution, it is probably not any faster (or slower) than not using shared pointers.

Comments

2

It's possible for pointers to subobjects - array elements or object members - to share ownership with pointers to the whole object. You create shared_ptrs to subobjects with the template constructor:

template< class Y > 
shared_ptr( const shared_ptr<Y>& r, T *ptr );

It accepts a shared_ptr of any type, and a pointer (possibly completely unrelated) to the target type. The constructed shared_ptr shares ownership with the source object r but when dereferenced points at ptr.

For example, create a pointer to the third array element of a std::array:

class foo {};
auto array = std::make_shared<std::array<foo, 16>>();
auto element_ptr = std::shared_ptr<foo>(array, array->data() + 2);

or to an object's data member:

struct foo {
  int i;
};
auto some_foo = std::make_shared<foo>();
auto foo_member = std::shared_ptr<int>(some_foo, &some_foo->i);

It's a shame there's no handy type-deducing "make" function like std::make_shared for this, it's annoying to explicitly specify the type of the subobject. Maybe we should write our own:

template <typename Object, typename SubObject>
inline std::shared_ptr<SubObject>
make_sub_ptr(const std::shared_ptr<Object>& c, SubObject* ptr) {
    return {c, ptr};
}

so we can simplify the earlier creations to:

auto element_ptr = make_sub_ptr(array, array->data() + 2);
// ...
auto foo_member = make_sub_ptr(some_foo, &some_foo->i);

Live Demo at Coliru

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.