简体   繁体   中英

Using std::move on std::shared_ptr<SomeClass> correctly

I am working on a project and dealing with shared_ptr 's. The program is working and giving the correct output. However upon looking at the logs i notice that at some points in the program the reference count is having a particular value. The sample reproducible program is shown below:

SAMPLE Program

#include <iostream>
#include<vector>
#include<string>
#include<utility>
#include<memory>
class NAME 
{   
    public:
    std::string name;
    int age = 12;
    
    
    NAME(std::string m_name, int m_age):name(m_name), age(m_age)
    {
        std::cout<<"NAME Parametrised "<<std::endl;
        
    }
    NAME(const NAME& rhs):name(rhs.name), age(rhs.age)
    {
        std::cout<<"NAME Copy constructor"<<std::endl;
    }
};
class SURNAME 
{
    public:
    std::vector<std::vector<std::shared_ptr<NAME>>> vec_2d;
    SURNAME(const std::vector<std::vector<std::shared_ptr<NAME>>> &m_vec):vec_2d(m_vec) 
    {
        std::cout<<"Refernce count in SURNAME para constructor: "<<vec_2d.at(0).at(0).use_count()<<std::endl;
    }
    
};
class AN
{
    public:
      std::vector<SURNAME> vec_SURNAME;
};

int main()
{

   
   std::vector<std::shared_ptr<NAME>> temp_vector;
   temp_vector.reserve(3);
   temp_vector.push_back(std::make_shared<NAME>("MIDDLE",43));
   temp_vector.push_back(std::make_shared<NAME>("LAST",54));
   temp_vector.push_back(std::make_shared<NAME>("ANOTHERLAST",54));
   std::vector<std::vector<std::shared_ptr<NAME>>> temp_2dvec;
   temp_2dvec.reserve(1);
   temp_2dvec.push_back(temp_vector);//reference count becomes 2
   
   AN obj;
   
   obj.vec_SURNAME.push_back(temp_2dvec);//reference count becomes 3

   return 0;
}

OUTPUT of the above program

NAME Parametrised 
NAME Parametrised 
NAME Parametrised 
Reference count in SURNAME para constructor: 3

As we can see in the output the reference count is 3 inside the constructor. I think that when we write temp_2dvec.push_back(temp_vector); the reference count is increased by 1 and then when we write obj.vec_SURNAME.push_back(temp_2dvec); the reference count again increases by 1 and finally becomes 3. Now my questions are:

  1. Is there a way to avoid this increase in reference count and instead move the share_ptr's? If yes then how? For example, i tried using temp_2dvec.push_back(std::move(temp_vector)); and then there is no increase in reference count in this step. Is doing this okay here in this step or is it some kind of UB?
  2. When i try to do the same and use the statement obj.vec_SURNAME.push_back(std::move(temp_2dvec)); then the reference count still increases even when i used std::move() here. How is this happening?
  3. Note that the variables temp_vector and temp_2dvec are just temporary variables that i made to hold the vectors. They are used only once for pushing purposes. So i will not use them. So it is safe to move from them. How can i safely achieve this, that is no increase in reference count should be there when pushing onto the vectors?
  4. Also, are my 2 comments correct(those which i wrote just to the right of statements in the main program)? That is, the reference count are increased at those 2 steps or are they increased at some other area/step of the program which i am missing.

I tried using temp_2dvec.push_back(std::move(temp_vector)); and then there is no increase in reference count in this step. Is doing this okay here in this step or is it some kind of UB?

std::move does not move or optimize anything by itself, it is just a type cast . But the code consuming moved object can handle it differentely because of different argument type. Standard library classes do it, eg for std::vector :

Move assignment operator. Replaces the contents with those of other using move semantics (ie the data in other is moved from other into this container). other is in a valid but unspecified state afterwards.

So, yes, you should use std::move and at least the standard library will handle it more effectively.

When i try to do the same and use the statement obj.vec_SURNAME.push_back(std::move(temp_2dvec)); then the reference count still increases even when i used std::move() here. How is this happening?

But your current code does not:

    std::vector<std::vector<std::shared_ptr<NAME>>> vec_2d;
    SURNAME(const std::vector<std::vector<std::shared_ptr<NAME>>> & m_vec):vec_2d(m_vec) 
    {
        std::cout<<"Refernce count in SURNAME para constructor: "<<vec_2d.at(0).at(0).use_count()<<std::endl;
    }

You have m_vec argument and copy it in vec_2d . here is where reference count = 2 comes from. you can handle moved objects more effectively by defining an additional separate constructor:

    SURNAME(std::vector<std::vector<std::shared_ptr<NAME>>> && m_vec):vec_2d(std::move(m_vec))
    {
        std::cout<<"Refernce count in SURNAME para constructor: "<<vec_2d.at(0).at(0).use_count()<<std::endl;
    }

here the argument type is different std::vector<std::vector<std::shared_ptr<NAME>>> && and it can be safely moved it instead of copying with vec_2d(std::move(m_vec)) .

Another approach is to have a single method that always get argument by value instead of having multiple overloads for copy and move:

    SURNAME(std::vector<std::vector<std::shared_ptr<NAME>>> m_vec):vec_2d(std::move(m_vec))
    {
        std::cout<<"Refernce count in SURNAME para constructor: "<<vec_2d.at(0).at(0).use_count()<<std::endl;
    }

here the argument is always moved, but the argument itself is either copy-constructed or move-constructed depending of the caller. this way you need to write only one method instead of two but this only helps when you always want to copy/move inside. if the copy needs to be conditional it is better to have separate overloads for copy and move.

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