【问题标题】:Returning a unique_ptr from a class method C++0x从类方法 C++0x 返回一个 unique_ptr
【发布时间】:2011-04-16 04:56:25
【问题描述】:

如果我的类 SomeType 有一个从地图返回元素的方法(使用键)说

std::unique_ptr<OtherType> get_othertype(std::string name)
{
   return otMap.find(name);
}

这将确保调用者会收到指向地图中的指针而不是副本的指针?这样做可以吗,还是会尝试调用复制构造函数(并且由于它已被删除而失败),因为它正在被返回?

假设我必须使用 unique_ptr 作为我的地图项。

更新::

在尝试实现代码后,似乎 unique_ptr 和 std:map/:pair 在 gcc 4.4.4 中不能一起工作,pair 只是不喜欢将 unique_ptr 作为类型参数。 (见Can't create map of MoveConstructibles)。

我将 ptr 更改为 std::shared_ptr 并且一切正常。

我想我可以对共享指针使用相同的代码吗?

【问题讨论】:

  • 顺便问一下,OtherType 到底是什么?它是具有虚拟成员函数的基类吗?你需要亚型多态吗?
  • 它将是一个像接口一样具有纯虚方法的基类。我只需要调用不需要强制转换的纯虚拟方法之一。这会改变事情吗?
  • 接口是unique_ptr 用法的主要示例。没有其他问题。
  • 感谢 Fred,最好花时间了解和使用最好的工具来完成这项工作,根据无效的假设盲目地在沙屋上建造。

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


【解决方案1】:

您是否考虑将地图更改为保存shared_ptrs 而不是unique_ptrs?这将使返回值更加安全。 unique_ptr 的全部意义在于它是独一无二的(即共享)。

【讨论】:

  • 重点是唯一的所有权。这并不排除其他原始指针指向同一对象的可能性。从这个意义上说,它与返回迭代器的容器相比并没有太大区别。
【解决方案2】:

unique_ptr 的模型是所有权转让。如果你从一个函数返回一个unique_ptr给一个对象,那么系统中没有其他unique_ptr可以引用同一个对象。

这就是你想要的吗?我对此表示高度怀疑。当然,你可以简单地返回一个原始指针:

OtherType* get_othertype(const std::string& name)
{
    return otMap.find(name)->second.get();
}

因此,客户端可以访问该对象,但地图仍然拥有它。

如果在名称下找不到条目,​​上述解决方案相当脆弱。更好的解决方案是在这种情况下抛出异常或返回空指针:

#include <stdexcept>

OtherType* get_othertype(const std::string& name)
{
    auto it = otMap.find(name);
    if (it == otMap.end()) throw std::invalid_argument("entry not found");
    return it->second.get();
}

OtherType* get_othertype(const std::string& name)
{
    auto it = otMap.find(name);
    return (it == otMap.end()) ? 0 : it->second.get();
}

为了完整起见,这里是 Anthony 的返回参考的建议:

OtherType& get_othertype(const std::string& name)
{
    auto it = otMap.find(name);
    if (it == otMap.end()) throw std::invalid_argument("entry not found");
    return *(it->second);
}

下面是您如何返回对地图内unique_ptr 的引用,但让我们将其设为对 const 的引用,这样客户端就不会意外修改原始内容:

unique_ptr<OtherType> const& get_othertype(const std::string& name)
{
    auto it = otMap.find(name);
    if (it == otMap.end()) throw std::invalid_argument("entry not found");
    return it->second;
}

【讨论】:

  • 那么执行此操作的标准方法是什么?我希望被调用者有一个指向对象的指针,但地图仍然是所有者。但是我不想使用原始的,因为被调用者可能不会处理它?如果我从他的地图中删除它会发生什么,他的原始文件将指向任何内容?
  • @Mark: 物体是否保证在地图中?如果是这样,那么你可以返回一个引用而不是一个指针,这样调用的语义就更明确了:我会给你访问权限而不是所有权。
  • @Mark:是的,并且在函数末尾“弹出”本地原始指针没有任何效果。为清楚起见,如果本地unique_ptr 被“弹出”,它所指的对象将立即被销毁。如果 shared_ptr 被“弹出”,则关联的引用计数减一,如果达到零,则销毁该对象。只是为了让您放心,鉴于您描述的用例,返回原始指针似乎是我的最佳选择。
  • 我建议您返回一个 reference 而不是原始指针。它清楚地表明地图拥有该对象。当然,在这种情况下,如果对象不存在,你最好扔掉。
  • @roysc 在第 23 行,auto r = ... 进行复制,即使 ... 通过引用返回。请改用auto&amp; r
【解决方案3】:

otMap的类型是什么?

如果otMap.find(name) 返回一个std::unique_ptr&lt;OtherType&gt; 作为右值,那么这将正常工作。但是,指向值的所有权现在已转移到返回的指针,因此该值将不再在映射中。这意味着您使用的是自定义地图类型,而不是 std::map&lt;&gt;

如果你想让map中的值并且返回一个指向它的指针,那么你需要使用std::shared_ptr&lt;OtherType&gt;作为map的值类型和@的返回类型987654327@.

std::map<std::string,std::shared_ptr<OtherType>> otMap;
std::shared_ptr<OtherType> get_othertype(std::string name)
{
    auto found=otMap.find(name);
    if(found!=otMap.end())
        return found->second;
    return std::shared_ptr<OtherType>();
}

【讨论】:

  • 我从这里 stackoverflow.com/questions/3759119/… 的另一个答案中得到了建议,其中 AshleysBrain 建议使用 unique_ptr 存储在集合中
  • shared_ptr 相对于unique_ptr 的开销很小(引用计数)。在这种情况下使用 unique_ptr 需要仔细考虑,我强烈建议不要使用它,除非它真的完全符合您的要求。
  • @Ant:我不同意。 unique_ptr 清楚地表达了程序员拥有对象的地图意图,它使代码可能更快。 shared_ptr 的正确实现必须考虑线程安全性才能获得正确的引用计数,这可能比 unique_ptr 慢很多。
  • 当然。将unique_ptr 放在地图中表示地图拥有该对象。但是,它给您留下了如何访问它的问题。如果您直接访问它,例如使用otMap[name]-&gt;something 那么没关系,但返回原始指针不太理想。 shared_ptr 的引用计数开销很小,除非对象实际上在线程之间共享。小到我不会担心,除非配置文件另有说明。
  • 如果他们这样做,那就是他们的问题。如果你给他们一个原始指针,那就是你的问题。我不喜欢给我原始指针的 API,因为不清楚谁拥有它,或者生命周期是什么。
【解决方案4】:

otMap.find 将返回一个右值,因此该右值将被移动,如果不是 RVO 的话。但是,当然,现在您的地图中没有那个特定的对象。另外,上次我检查时,find 返回一个迭代器,而不是值类型。

【讨论】:

  • 是的,我的错,但我希望你能明白
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-16
  • 2012-08-31
  • 1970-01-01
  • 2023-04-03
  • 2016-07-22
  • 1970-01-01
相关资源
最近更新 更多