【问题标题】:How to pass unique_ptr<T> in place of a raw *output* pointer parameter?如何传递 unique_ptr<T> 代替原始 *output* 指针参数?
【发布时间】:2017-06-28 12:37:24
【问题描述】:

我在 external 库中有一个 预先存在的 函数,它看起来像这样;

bool CreateTheThing(MyThing *& pOut);

简而言之;我给它一个原始指针(通过引用),该函数分配内存并将我的指针分配给新分配的对象。当函数返回时,我的有责任在我完成后释放内存。

显然,我想将此结果存储到unique_ptr&lt;MyThing&gt;,并避免手动使用delete

可以创建一个临时原始指针以用于 API 调用,并将其传递给 unique_ptr 的构造函数;

MyThing* tempPtr;
CreateTheThing(tempPtr);
unique_ptr<MyThing> realPtr = unique_ptr<MyThing>(tempPtr);

还有比这更直接的方法吗?一个不需要临时原始指针的?理想情况下,会有unique_ptr 的方法以一种可以直接与CreateTheThing 方法一起使用的方式公开其内部指针?

unique_ptr&lt;T&gt;::get() 不允许这样做,据我所知。它返回的指针不是对内部使用的指针的引用。

【问题讨论】:

  • 库是否要求您使用free() 来解除分配?
  • 有什么理由不只返回一个指针?
  • 啊,太糟糕了。外部库迫使你编写愚蠢的代码。
  • 您可以将realPtr行缩短为unique_ptr&lt;MyThing&gt; realPtr(tempPtr);
  • 是的,或者!:auto realPtr = unique_ptr&lt;MyThing&gt;(tempPtr)

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


【解决方案1】:

您可以通过一次编写多行代码来节省一行代码(可能多次):

class Wrapper
{
  std::unique_ptr<MyThing> &u;
  MyThing *p;

public:
  Wrapper(std::unique_ptr<MyThing> &u) : u(u), p() {}

  operator MyThing* & ()
  { return p; }

  ~Wrapper()
  { u.reset(p); }
};

用法:

std::unique_ptr<MyThing> u;
CreateTheThing(Wrapper(u));

【讨论】:

  • 整洁! template&lt;typename T&gt; class out_ptr_adapter;
  • @BTownTKD 加上 template &lt;class T&gt; make_out_ptr_adapter() 用于类型推断,当然。
  • p 应使用 nullptr 进行初始化,以确保异常安全。
  • @CAF p 是值初始化的(注意 ctor mem-initialiser 列表中的 p()),这意味着它将是一个空指针。
【解决方案2】:

还有比这更直接的方法吗?一个不需要临时原始指针的?

不,没有。

理想情况下,会有一个 unique_ptr 的方法,它以一种可以直接与 CreateTheThing 方法一起使用的方式公开其内部指针?据我所知,unique_ptr::get() 不允许这样做。

你的知识是正确的。这将破坏std::unique_ptr 的全部目的,因此unique_ptr::get() 是一个const 函数,并且指针按值返回。

尽管与您的构造函数示例类似,您始终可以使用std::unique_ptr::reset() 传递外部分配的指针。

另请注意:如果第 3 方 API 要求您使用 free() 释放内存,您可能需要为 std::unique_ptr 提供特殊的删除函数。

【讨论】:

  • 清晰、简洁和明确的答案。非常感谢!
  • nitpick:unique_ptr::get() 返回指针 by-value 是您无法更改通过它存储的内容的原因。
  • @Caleth Better?
【解决方案3】:

如果你经常使用这个函数,你可以把转换放在一个函数中。

最好是更改 API,但这也可以。

inline std::unique_ptr<MyThing> CreateTheThing()
{
  MyThing* p;
  if (CreateTheThing(p))
  {
    return std::unique_ptr<MyThing>(p);
  }
  return std::unique_ptr<MyThing>();
}

你也可以使这个重载更容易重构:

inline bool CreateTheThing(std::unique_ptr<MyThing>& ptr)
{
    try 
    {
        MyThing* p;
        if (CreateTheThing(p))
            ptr = std::unique_ptr<MyThing>(p);
        else
            return false;
    }
    catch (std::bad_alloc&)
    {
        return false;
    }
    return true;
}

【讨论】:

  • CreateTheThing() 是从外部库调用的函数。我怀疑 OP 可以简单地改变它。
  • 没错。为了清楚起见,我编辑了这个问题。我想避免更改外部库中的旧 API。
  • IMO 这是最好的答案,因为您可以使用它来强制应用程序的其余部分使用新方法,方法是将旧的API 隐藏在新的包装函数 API 后面。然后你就知道没有人会因为拥有原始指针而四处游荡。
  • 此方案不隐藏原始api。它提供了一个覆盖。没有办法隐藏全局函数。
  • 它不是虚拟的,所以它不会“覆盖”任何东西。但是,是的,它是一个重载,不会隐藏或以其他方式干扰原始CreateTheThing()。我不确定@Galik 是什么意思;也许他们可以澄清一下。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-05
  • 2019-12-13
相关资源
最近更新 更多