移动语义与返回shared_ptr?

时间:2017-08-21 22:44:33

标签: c++ c++11 move-semantics

我理解在C ++程序员中鼓励使用值语义。但在我的工作中,我注意到一些程序员使用引用语义的模式,确切地说,他们使用shared_ptr,我将使用值语义。

为了给这个提供一点上下文,例如,我有一个API,它读取数据库页面并返回其内容。我看到有两种方法可以做到。

选择1值语义:

DBPage readDatabasePage(int number) { // number is the for which page to read
    DBPage page;
    ... // reading the database page

    return page; // here we have RVO/move semantic to help us so it is not inefficient
} 

选择2参考语义:

std::shared_ptr<DBPage> readDatabasePage(int number) { // ditto
    std::shared_ptr<DBPage> page = std::make_shared<DBPage>();
    ...
    return page;
}

第二种选择对我来说似乎没问题,因为我看不出这样做的缺点。所以我想要理解的是为什么我们鼓励人们使用价值语义。选择2有什么问题?

3 个答案:

答案 0 :(得分:2)

默认值语义是首选,因为它没有分配内存=&gt;没有内存泄漏,没有损坏的内存等。

每个指针类型都有自己的语义。仅当shared_ptr所控制的资源将共享时才应使用shared_ptr,因此它最重要的是它将持续到最后一个引用(指针)。在您的示例中,DBPage不合适。如果希望在您的示例中使用指针(例如unique_ptr太大而无法存储在堆栈中),则它应为unique_ptr

您的示例可能不完整,之后结果确实会被分享&#34;。我要说的是,即使在这种情况下,shared_ptr也应该被使用,之后将被转换为&#34;到shared_ptr,否则功能签名会产生误导,有时它是用户唯一可见的东西。虽然在这种情况下它是有争议的。

此外,unique_ptrstd::move慢,并且比int.operator++慢得多,因为它使用原子操作进行引用计数。与简单{{1}}相比,它们非常昂贵,但它仅在性能危急情况下才会出现问题。

答案 1 :(得分:1)

这取决于DBPage的复制成本。例如,如果DBPage是一个包含一些指向数据的指针的类,那么复制它可能很便宜,而将它存储在shared_ptr中可能会增加不必要的开销。

另一方面,也许DBPage复制起来很昂贵。你提到RVO并移动语义,从函数返回DBPage时这些都很好,但是如果用户实际上想要保留两个引用相同数据的变量,并且他们希望数据的生命周期是两个变量的最大值#39;一生,然后shared_ptr是天生的契合。

如果用户最终需要shared_ptr<DBPage>但你给他们DBPage,他们可能需要复制数据才能获得他们想要的内容。

简而言之,您需要了解您的用户和正在使用的实际数据。

答案 2 :(得分:0)

您有两种不同的情况需要创建对象。你的价值语义&#34;在您希望某些内存成为返回对象(堆栈内存,数组中的内存等)的情况下,可以正常工作。你的&#34;参考语义&#34;当您希望对象的生命周期超出其范围(并在堆中生存)时非常有用。

要明确解决您的问题,当您想要将结果存储在std::vector<DBPage> 时,选项2将无法使用。即使使用移动语义,为工作提供正确的功能也是更好的选择。