我们如何从成员函数中 return 一个 unique_pointer 成员?

How do we return a unique_pointer member from a member function?

我有一个带指针成员的基 class。我将不得不做出有根据的猜测来确定它应该是 unique_ptr 还是 shared_ptr。 None 其中似乎解决了我的特定用例。

class Base
{
public:
    Base(): pInt(std::unique_ptr<int>(new int(10))) {};
    virtual std::unique_ptr<int> get() = 0;
    //Base(): pInt(std::shared_ptr<int>(new int(10))) {}; // Alternate implementation
    //virtual std::shared_ptr<int> get() = 0; // Alternate implementation
private:
    std::unique_ptr<int> pInt;
    //std::shared_ptr<int> pInt; // Alternate implementation
};

基数 class 已派生到 Derived1Derived2 上。前者 returns unique_ptr 成员 pInt 其中后者 returns 本地 unique_ptr 对象。

class Derived1: public Base
{
public:
    Derived1() {};
    virtual std::unique_ptr<int> get()
    {
        //return std::move(pInt);  Will compile but the ownership is lost
        return pInt;
    }
private:
    std::unique_ptr<int> pInt;
};
class Derived2: public Base
{
public:
    Derived2() {};
    virtual std::unique_ptr<int> get()
    {
        std::unique_ptr<int> pInt(new int());
        return pInt;
    }
private:
    std::unique_ptr<int> pInt;
};

Derived1 的 get 实现不会隐式转移所有权,因为成员指针变量不是 eXpiring 值,而 Derived2的执行即可。此行为在标准

中有详细记录

参见 12.8 §34 和 §35:

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object [...] This elision of copy/move operations, called copy elision, is permitted [...] in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type [...]

When the criteria for elision of a copy operation are met and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.

然而,如果我通过 std::move 显式转移所有权,成员指针将来将无法使用。

或者,我必须将指针定义为 shared_ptr,但这将是 Derived2::get.

实现的额外开销

注意 应该考虑到 Derived2::get 的出现次数比 Derived1::get 多所以使用 std:: shared_ptr 的设计决策可以具有相当大的相对影响。

unique_ptr 无法按照您希望的方式处理您的 Derived1 案件。您需要多个指向同一资源的智能指针。 unique_ptr 根本不是一个选择。没有办法解决这个问题。

您可以坚持使用 unique_ptr 成员,但使您的函数 return 成为原始指针。

virtual int *get() = 0;

这对你的Derived2class来说很麻烦,因为不清楚调用者是否应该释放指向的内存。我建议你不要这样做。

您可以按照您的建议使用 shared_ptr 成员,并使您的函数成为 return。这在您的 Derived2 class 中完全可用,但正如您指出的那样,不是最理想的。

不过,它仍然是最干净的解决方案。对于只知道他们有一个 Base 的呼叫者,您 需要 某种方式来通知他们(手动或通过 returned 类型)他们应该做什么当他们完成 get() 的结果时再做,所以你不能 return unique_ptr<int> 无论如何。

函数 returning unique_ptr<int> 唯一有用的方法是如果调用者已经知道你有一个 Derived2。但是,您可以添加一个新成员:

virtual shared_ptr<int> get() {
  return get_unique();
}
virtual unique_ptr<int> get_unique() {
    std::unique_ptr<int> pInt(new int());
    return pInt;
}

不过,只有在分析显示 shared_ptr<int> get() 成员实际上增加了可衡量的开销时,我才会这样做。您的 shared_ptr<int> 实施很有可能在性能方面就足够了,然后可读性可能是 添加新成员的原因。

unique_ptr的目的是从一个唯一的地方指向一个资源。如果你需要从多个地方指向这个资源,unique_ptr 就不再合适了。在这种情况下,您应该使用 shared_ptr。
如果释放资源的所有权适合您的逻辑,则只需使用:

unique_ptr<int> get()
{
   return move(pInt);// this will move the ownership of the resource away from the member field
}

...但是从现在开始,pInt 不再有效。

如果你对资源很小心(如果你不担心悬挂指针),而不是 return 指向资源的原始指针(但请不要更喜欢使用 shared_ptr ).

在使用shared_ptr的情况下,注意循环依赖,使用weak_ptr来对抗它。这是关于它的一些事情:http://geekwentfreak-raviteja.rhcloud.com/blog/2014/07/06/c11-how-to-create-cyclic-dependencies-with-shared_ptr-and-how-to-avoid-them/?_sm_au_=irVM6PVF1TR4nGMW

Post 编辑: 当您使用 unique_ptr 作为成员字段时,您正在丢失复制构造函数,因为无法复制 unique_ptr。仅供参考

但是,在我看来,使用 unique_ptr 已经是额外的开销,其中只需使用 shared_ptr 或直接使用 int。