This is more a styling than performance question. I have just converted (most of) my pointers to shared_ptr objects, and have reluctantly come to accept weak_ptrs as alternatives to raw pointers. My question is, what is the preferred method of iterating through a sequence (let's say a vector) of shared pointer objects? Here is what I've been doing:
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 goes against the *don't use shared_ptr references* rule though, so what's a good alternative, and why?
Questions I would be asking are; Is the technique robust, ie. For AMethod() super tiny, and my_sequence super large, will this method start to impede performance unecessarily due to shared_ptr copies? Is it readable? Is it simple?
First, a disclaimer:
shared_ptr
isn't a panacea. It should be used when ownership is actually shared. Non-sharing ownership is expressed by unique_ptr
and non-ownership is expressed by a (raw) reference. weak_ptr
is for a shared_ptr
that must be observed but not owned… but isn't a good defensive practice against stale pointers in general. Defaulting to shared_ptr
goes directly to the lowest common denominator; it's poor programming practice.
The choice here is between shared_ptr< T >
, shared_ptr< T const >
, shared_ptr< T > const &
, and T const &
, and T &
. Let's assume you're not changing anything, but the sequence could change its objects, so const
is desirable. That narrows it to shared_ptr< T const >
, shared_ptr< T > const &
, and T const &
.
Let's reformulate the question as,
Is
shared_ptr< T > const &
ever the right choice?
In this light, evaluate the alternatives.
shared_ptr< T const >
(pass shared pointer by value)
shared_ptr< T >
move
d to expand the ownership poolconst
correctness to the called functionshared_ptr< T > const &
(pass shared pointer by reference)
–: loses const
correctness to the called function
shared_ptr< T const > const &
instead, you will pass a temporary copy and end up with the disadvantages of this alternative and pass-by-value.–: access to object goes through an extra indirection
T const &
(direct reference)
const
correctnessWeighing it on balance, if ownership isn't being extended (such as by returning an object that retains the argument), you really should pass a simple reference. In such a case, the function should seldom care how its argument is owned. Having it require a shared_ptr
violates separation of concerns in the worst way. You can't have a local object or a member subobject and still use the function. And worst of all, everything is more complicated.
If ownership is potentially extended to someone else, which would be the justification for using shared_ptr
in the first place, then you should always pass by shared_ptr< [const] T >
value. Yes, it does copy the shared_ptr
upon call. But the expensive part of copying a shared_ptr
is updating the refcount, not pushing the pointers on the stack. And if you're conferring ownership, you want to update the refcount anyway. Take the passed value and move
it, which does not touch the refcount. Having done that, you're left with no advantages and a lot of disadvantages to pass-by-reference.