我们如何从成员函数返回unique_ptr成员?

时间:2015-02-16 10:53:03

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

我有一个带有指针成员的基类。我必须做出有根据的猜测,以确定它应该是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语句,具有类返回类型,当时   expression是具有的非易失性自动对象的名称   与函数返回类型[...]

相同的cv-unqualified类型      

当满足复制操作的省略标准时   要复制的对象由左值,重载决策指定   选择首先执行复制的构造函数就好了   对象由右值指定。

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

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

注意应该考虑将Derived2::get的出现与Derived1::get进行比较,因此使用std:: shared_ptr的设计决策会产生相当大的相对影响。

2 个答案:

答案 0 :(得分:5)

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

你可以坚持使用virtual int *get() = 0; 成员,但让你的函数返回一个原始指针。

Derived2

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

您可以按照建议使用Derived2成员,并让您的函数返回该成员。这在您的Base课程中完全有效,但正如您所指出的那样,次优。

但它仍然是最干净的解决方案。对于只知道他们有get()的来电者,您需要某种方式通知他们(手动或通过返回的类型)他们应该做什么。完成了unique_ptr<int>的结果,因此无论如何都无法返回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>成员实际上增加了可衡量的开销,我只会这样做。您的{{1}}实施充分,性能方面,然后可读性可能是添加新成员的原因。

答案 1 :(得分:0)

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。 FYI

但是,在我看来,使用unique_ptr作为额外开销,只需直接使用shared_ptr或int。