转移唯一所有权:unique_ptr与移动语义

Transferring sole ownership: unique_ptr vs move semantics

本文关键字:ptr 移动 语义 unique 唯一 所有权 转移      更新时间:2023-10-16

似乎std::unique_ptr解决了一个也可以通过移动语义解决的问题,即转移唯一拥有的资源的所有权。以下是一些他们似乎执行相同任务的例子:

class SomeResource { /* resourcey stuff */ };
void ProcessResourcePtr(std::unique_ptr<SomeResource> r) { /* do stuff */ }
void ProcessResourceRvalRef(SomeResource&& r) { /* do stuff */ }
class OwnerOfResourcePtr {
private:
    std::unique_ptr<SomeResource> r_;
public:
    OwnerOfResourcePtr(std::unique_ptr<SomeResource> r) : r_(std::move(r)) { }
};
class OwnerOfResourceRvalRef {
private:
    SomeResource r_;
public:
    OwnerOfResourceRvalRef(SomeResource&& r) : r_(std::move(r)) { }
};

int main()
{
    // transfer ownership to function via unique_ptr
    std::unique_ptr<SomeResource> r1(new SomeResource());
    ProcessResourcePtr(std::move(r1));
    // transfer ownership to function via rvalue ref
    SomeResource r2;
    ProcessResourceRvalRef(std::move(r2));
    // transfer ownership to instance via unique_ptr
    std::unique_ptr<SomeResource> r3(new SomeResource());
    OwnerOfResourcePtr(std::move(r3));
    // transfer ownership to instance via rvalue ref
    SomeResource r4;
    OwnerOfResourceRvalRef(std::move(r4));
    return 0;
}

在我看来,这两者以略微不同的方式解决了几乎完全相同的问题。我不是100%清楚一种方式与另一种方式的优势。我知道指针移动可能比移动构造函数/赋值更快,尽管通常认为两者都非常有效。我还知道,移动语义允许您将数据保留在堆栈上(请参阅r2,r4),而不需要使用new/malloc/etc进行堆分配/释放(请参阅r1,r3),我认为这是一件好事(是吗?)。

一般来说,什么时候应该更喜欢unique_ptr而不是移动语义,或者反之亦然?是否有任何用例只能由其中一个或另一个解决?

如果您有一个类(可能是由其他人编写的)没有移动构造函数,甚至可能没有复制构造函数,那么您可能必须使用unique_ptr

如果实例是可选的,即可能不存在,则应使用unique_ptr

如果只能在调用构造函数后初始化对象,并且该对象不是默认可构造对象,则必须延迟构造,并且可以为此使用unique_ptr

即使一个类有一个移动构造函数,也可能比移动单个指针更昂贵。例如,如果类包含数十个POD实例或一个数组。