【问题标题】:Using std::move on std::shared_ptr<SomeClass> correctly在 std::shared_ptr<SomeClass> 上正确使用 std::move
【发布时间】:2021-08-28 07:21:14
【问题描述】:

我正在处理一个项目并与shared_ptr 打交道。该程序正在运行并提供正确的输出。然而,在查看日志时,我注意到在程序中的某些点,引用计数具有特定值。示例可重现程序如下所示:

示例程序

#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;
}
上述程序的

输出

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

正如我们在输出中看到的,构造函数中的引用计数为 3。我认为当我们写temp_2dvec.push_back(temp_vector);时引用计数增加1,然后当我们写obj.vec_SURNAME.push_back(temp_2dvec);时引用计数再次增加1,最后变成3。现在我的问题是:

  1. 有没有办法避免这种引用计数的增加,而是移动 share_ptr 的?如果是,那怎么办?例如,我尝试使用temp_2dvec.push_back(std::move(temp_vector));,然后在此步骤中引用计数没有增加。在这一步中这样做可以吗?还是某种 UB?
  2. 当我尝试做同样的事情并使用语句obj.vec_SURNAME.push_back(std::move(temp_2dvec)); 时,即使我在这里使用 std::move(),引用计数仍然会增加。这是怎么回事?
  3. 请注意,变量temp_vectortemp_2dvec 只是我用来保存向量的临时变量。它们仅用于推送目的一次。所以我不会使用它们。所以离开他们是安全的。我怎样才能安全地实现这一点,即推入向量时不应该增加引用计数?
  4. 另外,我的 2 个 cmets 是否正确(我写在主程序语句右侧的那些)?也就是说,引用计数在这两个步骤中增加了,或者它们在我缺少的程序的其他一些区域/步骤中增加了。

【问题讨论】:

    标签: c++ c++11 c++14 shared-ptr smart-pointers


    【解决方案1】:

    我尝试使用 temp_2dvec.push_back(std::move(temp_vector));然后在这一步没有增加引用计数。在这一步中这样做可以吗?还是某种 UB?

    std::move 本身不会移动或优化任何东西,它只是一个type cast。但是由于参数类型不同,使用移动对象的代码可以不同地处理它。标准库类可以做到这一点,例如对于std::vector

    移动赋值运算符。使用移动语义将内容替换为 other 的内容(即 other 中的数据从 other 移动到此容器中)。 other 之后处于有效但未指定的状态。

    所以,是的,你应该使用std::move,至少标准库会更有效地处理它。

    当我尝试做同样的事情并使用语句 obj.vec_SURNAME.push_back(std::move(temp_2dvec));那么即使我在这里使用 std::move() 引用计数仍然会增加。这是怎么回事?

    但您当前的代码没有:

        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;
        }
    

    您有m_vec 参数并将其复制到vec_2d。这里是引用计数 = 2 的来源。您可以通过定义一个额外的单独构造函数来更有效地处理移动的对象:

        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;
        }
    

    这里的参数类型是不同的std::vector&lt;std::vector&lt;std::shared_ptr&lt;NAME&gt;&gt;&gt; &amp;&amp;,可以安全地移动它而不是用vec_2d(std::move(m_vec))复制。

    另一种方法是使用always get argument by value 的单一方法,而不是复制和移动的多个重载:

        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;
        }
    

    这里的参数总是被移动,但参数本身是复制构造的还是移动构造的,这取决于调用者。这样,您只需要编写一种方法而不是两种方法,但这仅在您总是想在其中复制/移动时才有帮助。如果复制需要有条件,最好为复制和移动设置单独的重载。

    【讨论】:

      猜你喜欢
      • 2020-11-04
      • 2015-06-21
      • 1970-01-01
      • 1970-01-01
      • 2018-03-06
      • 2017-06-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多