How to iterate through a sequence of shared_ptr objects?

This is more a style than a performance issue. I just converted (most) my pointers to shared_ptr objects and reluctantly came to accept weak_ptrs as an alternative to the original pointers. My question is: what is the preferred method of iterating through a sequence (say, a vector) of common pointer objects? Here is what I did:

std::vector<std::shared_ptr<A>> my_sequence; // Do something to fill my_sequence; for (std::shared_ptr<A> const& ptr : my_sequence) { ptr->AMethod(); } 

This is against the rules * not to use shared_ptr reference * links, but what a good alternative and why?

The questions I would ask; Is the technique reliable, i.e. For AMethod () is super tiny and my_sequence is super big, would this method interfere with performance unnecessarily due to shared_ptr copies? Is it readable? It's simple?

+7
source share
2 answers

First, disclaimer:

shared_ptr not a panacea. It should be used when the property is actually shared. Ownership without separation is expressed by unique_ptr , and unique_ptr is expressed by reference (raw). weak_ptr for shared_ptr , which should be respected but not owned ... but is not a good defensive practice against legacy pointers in general. By default, shared_ptr goes directly to the lowest common denominator; this is bad programming practice.


The choice here is between shared_ptr< T > , shared_ptr< T const > , shared_ptr< T > const & and T const & and T & . Suppose you don't change anything, but a sequence can change its objects, so const desirable. This narrows it down to shared_ptr< T const > , shared_ptr< T > const & and T const & .

We reformulate the question as

Is shared_ptr< T > const & right choice?

In this light, evaluate the alternatives.

  • shared_ptr< T const > (pass a shared pointer by value)

    • +: simple semantics of meaning; can pass shared_ptr< T >
    • +: You can copy or move d to expand the ownership pool.
    • +: extends const correctness of the called function
    • +: access to the object is quick: direct pointer argument is enabled
    • -: passing is expensive: copies two machine words and atomic touches ref ref
    • -: the called function refers to the semantics of ownership
  • shared_ptr< T > const & (pass a shared pointer by reference)

    • +: transfer is as cheap as a direct link to a direct object
    • +: can be copied to expand the ownership pool.
    • -: loses const correctness of the called function

      • If you try shared_ptr< T const > const & instead, you will go through a temporary copy and end up with the flaws of this alternative and the missing values.
    • -: access to the object passes through an additional indirect designation

    • -: the called function refers to the semantics of ownership
  • T const & (direct link)

    • +: keeps const correct
    • +: Caller is not interested in property semantics
    • +: fast, just one machine word
    • +: quick access, direct pointer to a function pointer (if even that)
    • -: cannot transfer ownership

Weighing on the balance sheet, if ownership does not expand (for example, returning an object that stores an argument), you really need to pass a simple link. In this case, the function should rarely care about how its argument belongs. If it requires shared_ptr , breaks the separation of problems in the worst case. You cannot have a local object or a member subobject and still use this function. And worst of all, everything is more complicated.

If ownership potentially extends to someone else, which would justify using shared_ptr in the first place, then you should always pass the value shared_ptr< [const] T > . Yes, it copies shared_ptr on call. But the expensive part of copying shared_ptr updates the refcount, rather than pushing pointers to the stack. And if you give ownership, you still want to update it. Take the passed value and move , which does not concern refcount. By doing this, you will be left without any advantages and many disadvantages for passing by reference.

+6
source

The reason for not using shared_ptr links is because you are breaking the mechanism from which it is trying to protect itself. That is, with a potentially hanging pointer. If you iterate through a container, this should not be a problem, as the objects will not disappear unexpectedly. You just should not keep the shared_ptr link, which you can use later.

those. This is bad:

 struct Y { int y; Y() : y(1) {} }; struct X { shared_ptr<Y>& ref_shared_ptr_y; X(shared_ptr<Y>& y) : ref_shared_ptr_y(y) {} }; void main() { shared_ptr<Y> shared_ptr_y(new Y()); X x(shared_ptr_y); y.reset(); x.ref_shared_ptr_y->y; // UB since x.ref_shared_ptr_y was deleted } 

shared_ptr should only be used if you really need to own objects for sharing between two or more locations. This leads to unnecessary overhead otherwise and shows that you really did not think about the relationship of the owner. If you own one place, use unique_ptr .

+8
source

All Articles