I am trying to understand which of the following usage of shared pointer makes more sense as it gets inserted into a vector.
bar
takes a const reference of a shared pointer vs foo
that takes a copy. In both the cases, the passed-in parameter gets moved into a vector. The interesting part is the use_count
of a
in the caller remains 2 for foo
and bar
both which implies the the vector stores a "copy"?
Like if a shared_ptr is passed by a reference, its count doesn't increment. As soon as it's "moved" into a vector, it does. Does that mean vector isn't storing the reference to an original object a
?
class A
{
std::vector<std::shared_ptr<int>> _vec;
public:
void bar(const std::shared_ptr<int>& ptr)
{
_vec.emplace_back(std::move(ptr));
}
void foo(std::shared_ptr<int> ptr)
{
_vec.emplace_back(std::move(ptr));
}
};
int main()
{
auto sp = std::make_shared<int>();
A a;
// a.foo(sp); // use_count = 2
a.bar(sp); // use_count = 2
}
CodePudding user response:
You're passing the shared_ptr
to bar
by reference to const
. That means that the original shared_ptr
can't be modified via that reference.
Moving from a share_ptr
requires modifying the moved-from shared_ptr
to set it to point to nothing.
See the issue? bar
can't move from ptr
, so it instead ends up copying it into the vector. ptr
/sp
remains valid and continues to point to the int
that you allocated in main
and a._vec
also holds a shared_ptr
to that same int
. Thus use_count
must be 2.
If you want to actually move from sp
then you should change bar
to accept some sort of mutable reference. Really you should make it accept an rvalue reference though, since a.bar(sp)
causing sp
to become invalid would violate most programmers' expectations:
class A
{
std::vector<std::shared_ptr<int>> _vec;
public:
void bar(std::shared_ptr<int>&& ptr)
{
_vec.emplace_back(std::move(ptr));
}
};
int main()
{
auto sp = std::make_shared<int>();
A a;
a.bar(std::move(sp));
// Here sp.use_count() is 0 because it was moved from
// a._vec.back().use_count() will be 1 though
}
This limits the caller to always moving their shared_ptr
into A
. Simply accepting the parameter by value as you do in foo
will likely result in no measurable performance difference when the caller wants to give up ownership and provides greater flexibility when they don't.
CodePudding user response:
In both cases, the shared pointer being passed to foo and bar is being moved into the vector. This means that the vector takes ownership of the dynamically allocated memory that the shared pointer was managing, and the shared pointer in the caller is no longer managing any memory.
The difference between foo and bar is that foo takes a copy of the shared pointer, while bar takes a const reference to the shared pointer. Since foo takes a copy, the shared pointer's reference count is incremented by one when it is passed to foo. This means that the reference count will be 2 after foo is called.
In contrast, bar takes a const reference to the shared pointer, so the reference count is not incremented when it is passed to bar. This means that the reference count will remain at 1 after bar is called.
Overall, the use of bar is more efficient in this case because it does not require an extra increment of the reference count, but the difference in performance is likely to be small in practice. It is more important to choose the approach that is more readable and maintainable for your specific use case.