简体   繁体   中英

C++ shared pointers referencing an object within an array

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?

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.

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 (ie another new for example) then it becomes very tedious to handle each call (ie 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.

It's possible for pointers to subobjects - array elements or object members - to share ownership with pointers to the whole object. You create shared_ptr s 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM