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

Bri*_*nam 7 c++ unique-ptr move-semantics c++11

这似乎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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

Joh*_*nck 6

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

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

如果你只能在你的构造函数被调用后初始化一个对象,并且该对象不是默认可构造的,你必须延迟构造,并且可以unique_ptr用于此。

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

  • 如果实例是可选的,您应该使用“std::experimental::Optional”或“boost::Optional”。为此使用“std::unique_ptr”是没有意义的。另外,我认为OP的问题根本没有道理。`std::unique_ptr` 显示您可以重用的所有权策略包。感觉就像归结为“我什么时候应该使用 `std::unique_ptr` 而不是我自己的所有权策略?”。 (2认同)
  • @Rapptz:`std::experimental` 不是任何标准的一部分。“TR1”从来都不是任何标准的一部分。它们只是实验。您可以将它们视为标准委员会说:我们对这个 API 感兴趣,我们正在寻求更多的现场经验和反馈。事实上,这就是为什么我努力游说命名空间“实验性”而不是容易被误解的“tr1”。 (2认同)