【问题标题】:unique_ptr behaviour while passing through function通过函数时的 unique_ptr 行为
【发布时间】:2015-10-24 12:47:17
【问题描述】:

在下面的代码 sn-p 中,我在传递 unique_ptr 作为值时遇到分段错误。通常这是 auto_ptr 的已知问题,因为所有权问题(受让人指针变为 NULL)在分配后无法访问它。 但是为什么我面临与 unique_ptr 相同的问题,即使它具有移动语义。

我理解 auto_ptr 使用复制构造函数和 unique_ptr 使用移动函数来转移所有权。但是在这两种情况下,受让人指针都变为空,那么这里有移动语义的意义是什么。我相信如果 unique_ptr 作为如下语句中的值传递,编译器应该会闪现错误,因为复制构造函数在 unique_ptr 中是私有的。您能否就这种行为对此有所了解。

unique_ptr<Test> a = p;//deleted function (copy constructor).

这里是sn-p的代码:

#include<iostream>

#include<memory>

using namespace std;

class Test
{
public:
    Test(int a = 0 ) : m_a(a)
    {
    }
    ~Test( )
    {
        cout<<"Calling destructor"<<endl;
    }

    int m_a;
};


//***************************************************************
void Fun(std::unique_ptr<Test> p1 )
{
    cout<<p1->m_a<<endl;
}
//***************************************************************
int  main( )
{
    std::unique_ptr<Test> p( new Test(5) );
    Fun(std::move(p));
    cout<<p->m_a<<endl;

    return 0;
}

【问题讨论】:

  • 你仍然可以通过传递引用来访问它。
  • 对于unique_ptrmove,您必须明确了解所有权转让。
  • rust,类似的代码会产生编译错误:-)
  • 并且拥有 unique_ptr 所有权在编译时处理。
  • 您将对象的所有权移动到函数中(由显式移动表示),将原始指针留空(nullptr),然后取消引用该指针。您期望会发生什么,为什么要将唯一的 ptr 传递给函数?一个正常的或会做得很好。

标签: c++ smart-pointers unique-ptr auto-ptr


【解决方案1】:

通过写入std::move(p),您将p 转换为r 值引用。 unique_ptr 有一个构造函数,它接受一个右值引用(一个 move 构造函数),所以它使用这个 move 构造函数构造了按值参数,并且没有编译错误。

除非您想转让所有权,否则不应按值传递unique_ptr

如果您转让所有权,则外部范围内的 unique_ptr 不再拥有任何东西,取消引用它是未定义的行为。

在这种情况下,您应该通过 const 引用传递 Test

void fun(const Test& t) {
    std::cout << t.m_a << std::endl;
} 

int main(){
    auto p = std::make_unique<Test>(5);
    fun(*p);
}

有关如何传递智能指针的更多信息,请参阅here

【讨论】:

  • +1 表示此解释。我只是想知道,为什么我们不能通过使用 auto_ptr (现在已弃用)来达到同样的效果。似乎是在复制构建期间 RHS 受让人对象设置为 NULL。
  • @user2997518 我对auto_ptr 不太熟悉,但我的理解是,因为它是在移动语义存在之前实现的,所以它们将“移动”行为作为复制的一部分来实现。这不是一个理想的解决方案,因为它很难在需要正常复制语义的通用代码中使用auto_ptr,但他们现在无法更改它,否则会破坏大量现有代码。所以他们选择弃用auto_ptr,而不是更新它以正确使用移动语义。如果您非常小心,您可能可以使用auto_ptr 实现相同的效果,但使用unique_ptr 会更清晰。
  • 下面的链接非常有用,它讨论了 r-value reference (&&operator)。 stackoverflow.com/questions/3106110/what-are-move-semantics
【解决方案2】:

这里不涉及删除的复制构造函数。 unique_ptr 使用了一个移动构造函数,因为您使用 std::move 明确请求从源移动。

您不应该使用已移动的值,除非将其销毁并分配给它。

这不是错误。编译器可能会发出警告,但由于您已明确移动,它可能会合理地假设您知道自己在做什么。

【讨论】:

    【解决方案3】:

    不要使用 unique_ptr 作为值,而是将其作为引用传递。 您创建一个唯一的指针。然后,您使用 move 构造函数从那个中构造另一个 unique_ptr。所以原始设置为空。这就是移动 unique_ptr 时发生的情况。老实说,因为我没有机会与 auto_ptr 合作,所以无法评论这种行为。

    void Fun( const std::unique_ptr<Test>& p1 )
    {
     cout<<p1->ma;
    }
    

    【讨论】:

    猜你喜欢
    • 2014-10-22
    • 2019-11-18
    • 2020-01-20
    • 2021-05-06
    • 1970-01-01
    • 2014-08-03
    • 1970-01-01
    • 1970-01-01
    • 2017-11-09
    相关资源
    最近更新 更多