微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在 std::shared_ptr<SomeClass> 上正确使用 std::move

如何解决在 std::shared_ptr<SomeClass> 上正确使用 std::move

我正在处理一个项目并与 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;
}
上面程序的

OUTPUT

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 条评论是否正确(我写在主程序语句右侧的那些)?也就是说,引用计数在这 2 个步骤中增加,或者它们在我缺少的程序的其他区域/步骤中增加

解决方法

我尝试使用 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<std::vector<std::shared_ptr<NAME>>> &&,可以安全地移动它而不是用 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;
    }

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。