如何将 std::unique_ptr 传递到函数中

How can I pass std::unique_ptr into a function

本文关键字:函数 ptr std unique      更新时间:2023-10-16

如何将std::unique_ptr传递到函数中?假设我有以下类:

class A
{
public:
    A(int val)
    {
        _val = val;
    }
    int GetVal() { return _val; }
private:
    int _val;
};

以下内容不编译:

void MyFunc(unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}
int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(ptr);
    return 0;
}

为什么我不能将std::unique_ptr传递到函数中?这肯定是构造的主要目的吗?还是C++委员会打算让我回退到原始的 C 样式指针并像这样传递它:

MyFunc(&(*ptr)); 

最奇怪的是,为什么这是一种可以传递它的方式?这似乎非常不一致:

MyFunc(unique_ptr<A>(new A(1234)));

这里基本上有两个选项:

通过引用传递智能指针

void MyFunc(unique_ptr<A> & arg)
{
    cout << arg->GetVal() << endl;
}
int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(ptr);
}

将智能指针移动到函数参数中

请注意,在这种情况下,断言将成立!

void MyFunc(unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}
int main(int argc, char* argv[])
{
    unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
    MyFunc(move(ptr));
    assert(ptr == nullptr)
}

您按值传递它,这意味着制作副本。这不会很独特,不是吗?

您可以移动该值,但这意味着将对象的所有权及其生存期的控制权传递给函数。

如果对象的生存期保证在调用 MyFunc 的生存期内存在,则只需通过 ptr.get() 传递原始指针。

为什么不能将unique_ptr传递到函数中?

您不能这样做,因为unique_ptr有一个移动构造函数,但没有一个复制构造函数。根据标准,当定义了移动构造函数但未定义复制构造函数时,将删除复制构造函数。

12.8 复制和移动类对象

7 如果类定义未显式声明复制构造函数,则隐式声明一个。如果类定义声明移动构造函数或移动赋值运算符,则隐式声明的复制构造函数定义为已删除;

可以使用以下命令将unique_ptr传递给函数:

void MyFunc(std::unique_ptr<A>& arg)
{
    cout << arg->GetVal() << endl;
}

并像您一样使用它:

void MyFunc(std::unique_ptr<A> arg)
{
    cout << arg->GetVal() << endl;
}

并像这样使用它:

std::unique_ptr<A> ptr = std::unique_ptr<A>(new A(1234));
MyFunc(std::move(ptr));

重要说明

请注意,如果使用第二种方法,则在调用 std::move(ptr) 返回后,ptr 没有指针的所有权。

void MyFunc(std::unique_ptr<A>&& arg)将具有与void MyFunc(std::unique_ptr<A>& arg)相同的效果,因为两者都是引用。

在第一种情况下,ptr在调用 MyFunc 后仍然拥有指针的所有权。

由于MyFunc不拥有所有权,因此最好拥有:

void MyFunc(const A* arg)
{
    assert(arg != nullptr); // or throw ?
    cout << arg->GetVal() << endl;
}

或更好

void MyFunc(const A& arg)
{
    cout << arg.GetVal() << endl;
}

如果你真的想获得所有权,你必须移动你的资源:

std::unique_ptr<A> ptr = std::make_unique<A>(1234);
MyFunc(std::move(ptr));

或直接传递 r 值引用:

MyFunc(std::make_unique<A>(1234));

std::unique_ptr没有故意复制以保证只有一个所有者。

为什么我不能将unique_ptr传递到函数中?

你可以,但不能通过复制 - 因为std::unique_ptr<>不是可复制构造的。

这肯定是构造的主要目的吗?

除此之外,std::unique_ptr<>旨在明确标记唯一所有权(而不是std::shared_ptr<>)。

最奇怪的是,为什么这是一种可以传递它的方式?

因为在这种情况下,没有复制构造。

由于unique_ptr用于唯一所有权,如果您想将其作为参数传递,请尝试

MyFunc(move(ptr));

但在那之后,mainptr状态将是nullptr.

为了捎带现有的答案,C++智能指针与所有权的概念密切相关。你的代码应该清楚地表达你与将所有权传递给或不传递给另一个组件(即函数、线程等)相关的信息,这就是为什么有独特的、共享的和弱指针的原因。唯一指针已经表明,在某一时刻只有一个组件拥有该指针的所有权,因此唯一所有权不能共享,只能移动。也就是说,当前拥有指针的所有者在脱离上下文时会自行销毁它,通常在块的末尾或在析构函数中。将对象传递给另一个函数可能意味着共享或移动所有权。如果您确定所有者在通过引用调用函数时不会脱离上下文,则可以通过引用调用,但这很丑陋。它添加了一些前置条件和后置条件,会增加代码的不动性,同时还会破坏指针的智能性,指针或多或少地调整为原始指针。例如,更改"函数"以在另一个线程中启动和执行自己的内容不再可能。由于在代码的某些非常包含的部分中,通过引用传递仍然是一种选择,即寻找节省从唯一指针到共享指针来回移动的额外开销,因此我会高度避免它,尤其是在公共接口中。

std::unique_ptr<T>作为值传递给函数是行不通的,因为正如你们提到的,unique_ptr是不可复制的。

这个呢?

std::unique_ptr<T> getSomething()
{
   auto ptr = std::make_unique<T>();
   return ptr;
}

此代码正在工作