Reputation: 1
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:
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?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?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 ?Upvotes: 0
Views: 799
Reputation: 2963
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, e.g. for std::vector:
Move assignment operator. Replaces the contents with those of other using move semantics (i.e. 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.
Upvotes: 1