【问题标题】:How do we return a unique_pointer member from a member function?我们如何从成员函数返回一个 unique_ptr 成员?
【发布时间】:2015-04-16 20:46:39
【问题描述】:

我有一个带有指针成员的基类。我必须做出有根据的猜测来确定它应该是unique_ptr 还是shared_ptr。它们似乎都不能解决我的特定用例。

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

基类已派生到Derived1Derived2。前者返回unique_ptr成员pInt,后者返回本地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:

当满足某些条件时,允许省略实现 类对象的复制/移动构造 [...] 复制/移动操作,称为复制省略,被允许在 [...] 具有类返回类型的函数中的 return 语句,当 表达式是非易失性自动对象的名称,其 与函数返回类型相同的 cv 非限定类型 [...]

当满足省略复制操作的条件并且 要复制的对象由左值指定,重载决议为 首先执行选择副本的构造函数,就好像 对象由右值指定。

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

或者,我必须将指针定义为shared_ptr,但这对于实现Derived2::get 来说是额外的开销。

注意 应该考虑到Derived2::get的出现比Derived1::get更多,所以使用std:: shared_ptr的设计决策会产生相当大的相对影响。

【问题讨论】:

  • 你没有说出你想要达到的目标,所以很难提出选择。您是否考虑过 (a) 返回一个原始指针,如果它只是需要被观察; (b) 返回一个弱指针?
  • @ForEveR 然后你会得到一个资源 拥有 同时由 2 个不同的智能指针类。当一个删除它时,另一个会做什么?
  • @RichardCritten 什么都没有。采用unique_ptrshared_ptr 构造函数获取指向对象的所有权,而unique_ptr 释放所有权。如果pInt 旨在保留所有权,但不是出于您提供的原因,这不是一个好主意。
  • get()返回一个unique_ptr的引用怎么样?

标签: c++ pointers c++11 shared-ptr unique-ptr


【解决方案1】:

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

您可以坚持使用unique_ptr 成员,但让您的函数返回一个原始指针。

virtual int *get() = 0;

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

您可以按照您的建议使用shared_ptr 成员,并让您的函数返回该成员。这在您的 Derived2 类中是完全可用的,但正如您指出的那样,不是最佳的。

不过,它仍然是最干净的解决方案。对于只知道他们有 Base 的呼叫者,您需要以某种方式通知他们(手动或通过返回的类型)他们在完成 get() 后应该做什么的结果,所以无论如何你都不能返回unique_ptr&lt;int&gt;

返回unique_ptr&lt;int&gt; 的函数可能有用的唯一方法是调用者已经知道你有一个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&lt;int&gt; get() 成员实际上增加了可测量的开销,我只会这样做。很有可能您的 shared_ptr&lt;int&gt; 实现已经足够,性能方面,然后可读性应该是添加新成员的原因。

【讨论】:

  • 我喜欢你的回答。在接受您的答案之前,我将等待一天,看看我是否确实得到了任何新的相关答案。再次感谢您的回答。
【解决方案2】:

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 不再有效。

如果您对资源很小心(如果您不担心悬空指针),那么不要只返回指向资源的原始指针(但请不要更喜欢使用 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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-23
    • 1970-01-01
    • 2019-09-03
    • 2019-06-26
    • 1970-01-01
    • 2022-01-10
    相关资源
    最近更新 更多