【问题标题】:If a function returns a std::unique_ptr, is it reasonable to assume each returned value is to a new object?如果一个函数返回一个 std::unique_ptr,假设每个返回值是一个新对象是否合理?
【发布时间】:2020-12-02 20:15:45
【问题描述】:

我正在了解std::unique_ptr,试图了解它所代表的含义。

给定一个返回unique_ptr 的函数(我无法控制),是否暗示/很好理解每次调用都会返回一个指向新对象的unique_ptr(与之前的任何调用不同)?

例如,下面的代码在退出时会产生一个双释放,我希望我能正确理解原因:unique_ptrs 在销毁时删除它们的底层对象;因此,封装相同内存/对象的两个unique_ptrs 会导致第二个被破坏时双重释放。因此,函数getUniquePtr() 的以下实现是否会被普遍/隐含地理解为不合理?

// main.cpp
#include <memory>
#include <iostream>

std::unique_ptr<int> getUniquePtr() {
  static int* p = new int(42);
  return std::unique_ptr<int>(p);
}

class PtrOwner {
public:
  std::unique_ptr<int> p_;
};

int main( int argc, char* argv[] ) {
  PtrOwner po1;
  PtrOwner po2;
  po1.p_ = getUniquePtr();
  po2.p_ = getUniquePtr();

  return 0;
}

【问题讨论】:

  • 是的,每个unique_ptr应该指向一个唯一的对象。如果不是,那么他们要么使用自定义删除器,您可以从它的签名中看出,因为删除器是模板参数的一部分,要么处理它,要么代码有错误。
  • 您发布的代码有一个错误 - p 将是 freed 两次。
  • 一个返回 unique_ptr 的函数仅表明这是对象的唯一拥有句柄。虽然工厂确实是一个非常常见的用例,但通常它不一定是刚刚创建的新对象。构建一个将 unique_ptr 传递回最初创建它的组件的 API 并非不可能,然后组件在特定情况下将所有权授予其他调用者。考虑智能指针所暗示的所有权而不是创建对象的时间会更有益。

标签: c++ unique-ptr double-free


【解决方案1】:

应该假设,如果一个函数返回std::unique_ptr&lt;T&gt;,那么返回的智能指针指向一个当前未被其他人管理的对象。这并不一定意味着它总是指代不同的对象。只要遵循这个约定,就可以避免双重释放错误。如果违反了这个约定,就会出现双释放错误。

例如,如果你看到这样的函数:

std::unique_ptr<T> foo(std::unique_ptr<T> arg);

在某些情况下,此函数可能会返回 std::move(arg),或者它可能会破坏 arg 并返回一些其他指针。 (您必须阅读文档才能知道它的作用)。这意味着您可以执行以下操作:

auto t = std::make_unique<T>();
t = foo(std::move(t));
t = foo(std::move(t));

在这种情况下,foo 可能只返回两次相同的指针值,这是非常安全的。这个例子看起来很傻,但希望它能理解我的意思。

【讨论】:

    【解决方案2】:

    是的,您的假设(大部分)是正确的。一个unique_ptr 独占拥有该对象,这意味着在任何给定时间都不能有两个unique_ptrs 指向同一个对象。

    正如其他人所指出的,这并不能保证对同一函数的多次调用会返回不同的对象。这是因为您可以传递所有权(通过移动它),所以如果您将所有权传递回一个函数,就像 Brians 的回答一样,它很可能会再次返回同一个对象,而不会违反任何规则。

    如果您决定使用智能指针来管理对象,请尽量避免使用 new 和 delete。要创建由unique_ptr 拥有的对象T,请使用make_unique&lt;T&gt;。这避免了为同一个对象创建多个unique_ptr 的错误。正是因为这个原因,unique_ptr 是不可复制的。换句话说,你的 getUniquePtr 应该这样实现:

    return std::make_unique<int>(42);
    

    从原始指针(而不是使用 make_unique)构造智能指针的少数原因之一是,当您拥有无法更改的现有代码或使用原始指针的库时。

    【讨论】:

      猜你喜欢
      • 2017-09-10
      • 2011-11-13
      • 1970-01-01
      • 2021-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-18
      相关资源
      最近更新 更多