Home > database >  Shared pointer passed by ref, copy and moved into a vector
Shared pointer passed by ref, copy and moved into a vector

Time:12-14

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
}

Demo


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.

  • Related