转移独占所有权:unique_ptr vs 移动语义

8

看起来std::unique_ptr解决了一个也可以通过move语义解决的问题,即转移唯一拥有资源的所有权。以下是一些例子,它们似乎执行相同的工作:

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;
}

依我之见,这两种方法基本上是以稍微不同的方式解决几乎相同的问题。我并不完全清楚一种方式与另一种方式的优势。我知道指针移动可能比移动构造函数/赋值更快,尽管两者通常被认为非常高效。我也知道移动语义允许您将数据保留在堆栈上(参见r2、r4),而不需要使用new/malloc等进行堆分配/释放(参见r1、r3),我认为这是一件好事(是吗?)。
总体上,在什么情况下应该优先选择unique_ptr而不是移动语义,反之亦然?是否有任何用例只能通过其中一种方法来解决?
1个回答

6

如果您有一个(可能由他人编写的)类,没有移动构造函数,甚至没有复制构造函数,则可能需要使用unique_ptr

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

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

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


2
如果实例是可选的,应该使用std::experimental::optionalboost::optional。使用std::unique_ptr没有意义。此外,我认为OP的问题甚至没有意义。std::unique_ptr显示了一个您可以重用的所有权策略包。感觉就像是“何时应该使用std::unique_ptr而不是我的所有权策略?” - Rapptz
@Rapptz:std::unique_ptr是标准C++的一部分,对于某些人来说,这是与“experimental”或“boost”相比的一个明显优势。我同意OP似乎在寻找避免使用std::unique_ptr的借口,而实际上它非常有用。 - John Zwinck
std::experimental::optional 在技术上也是标准的一部分。就像 TR1 中的东西在当时也是标准的一部分一样。Clang 和 GCC 都支持 std::experimental::optional - Rapptz
你们两个都是对的;我无意中创建了自己的所有权策略,但感觉有些不对劲,因为我没有使用 unique_ptr(如果不明显,我对 c++ 还很新)。我知道 unique_ptr 一定有一些优势,否则他们就不会费心去制作它,但由于我的策略还没有引起任何问题,所以我没有看到这些优势。@John Zwinck 很好地描述了我的策略将来会遇到的痛点。@Rapptz,我会阅读各种 optional 包的相关内容。无论你是否使用它们,了解它们似乎都是一个好主意。谢谢大家。 - Brian Putnam
2
@Rapptz:std::experimental不是任何标准的一部分。TR1从未成为任何标准的一部分。它们只是实验。您可以将它们视为标准委员会表示:我们对此API感兴趣,并且正在寻求更多的现场经验和反馈。确实,这就是为什么我强烈游说使用命名空间“experimental”,而不是容易被误解的“tr1”的原因。 - Howard Hinnant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接