【问题标题】:Taking ownership from a smart pointer with further access to the raw pointer via the same smart pointer从智能指针获取所有权,并通过相同的智能指针进一步访问原始指针
【发布时间】:2014-09-25 05:39:11
【问题描述】:

我有一个这样的泄漏代码:

void f() {
  SomeClass *sc = new SomeClass;
  ...
  if (...) {
    g(sc); // g() takes ownership of memory pointed to by sc
  }
  ...
  sc->SomeMethod();
  ...
}

我想通过使用智能指针来消除泄漏,想到了以下解决方案:

void f() {
  smart_ptr<SomeClass> sc(new SomeClass);
  ...
  if (...) {
    g(sc.release()); // ownership is taken
  }
  ...
  sc->SomeMethod(); // sc points to the allocated memory regardless of whether ownership was taken or not
  ...
} // the memory is freed if ownership was not taken

尝试使用 std::unique_ptr 和 std::shared_ptr。 std::unique_ptr 指向释放后的空指针, std::shared_ptr 根本没有 release() 。手动增加 std::shared_ptr 的引用计数器会有所帮助,但据我所知,也没有这样的能力。

除了原始指针保持原样保留代码并且每次将原始指针传递给获得所有权的函数时调用 release() 之外,还考虑使用 std::unique_ptr,但这是一个非常混乱的解决方案。

我想知道标准库中是否有适合我目标的智能指针, 或者在我的情况下可能有一个理念或常见的技巧,或者我可能遗漏了什么?

【问题讨论】:

  • 只需使用unique_ptr 和原始指针,这是最简单的解决方案。在通过原始指针调用 SomeMethod() 之前,您确实需要确保 g() 不是 delete 对象。
  • 是的,解决方案最简单但容易出错。代码是开源的,我想使用一个变量。 f()的实体很大,指针使用频繁,所有权多次转让。

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


【解决方案1】:

您可以使用shared_ptr 获得相同的效果。 shared_ptr 没有 release() 函数,但它有 reset() 函数,我认为您正在寻找它。但是,您必须等到调用SomeClass::SomeMethod() 之后才能调用sc.reset()

void g(shared_ptr<SomeClass> ptr);

void f()
{
   shared_ptr<SomeClass> sc(new SomeClass);

   if (/* check for condition */)
   {
      g(sc);
   }

   sc->SomeMethod();
   sc.reset();
}

【讨论】:

  • 应谨慎考虑使用共享指针;它们通常表明数据结构考虑不周,并且比unique_ptr 具有更多开销,因为它们必须是线程安全的。即使g 确实需要shared_ptrsc 也不需要是shared_ptrunique_ptr 可隐式转换为 shared_ptr(尽管您需要将 g(sc) 更改为 g(std::move(sc)))。这样,您只会在实际调用 g() 时产生 shared_ptr 开销。
  • 另外,sc.reset() 是不必要的,因为无论如何sc 会在之后立即被销毁。
  • 请注意,如果您将sc 更改为unique_ptr,您还应该使用上面KerrekSB 描述的本地视图技术。
  • 是的,sc.reset() 在这里似乎是不必要的。还要注意:g 被声明为 g(SomeClass *),我无法对其进行编辑。
【解决方案2】:

我不明白您所说的 g 取得所有权是什么意思,因为在 g 退出后,您仍将使用指针。如果您总是想在g 之后使用指针,则将原始指针传递给g 并在块末尾释放指针会更有意义。

如果您确实希望g 获得所有权,并且希望仅在您的指针仍然存在时调用SomeMethod,请将原始指针传递给g 并让它返回一个原始指针。如果指针在g 中被释放,则返回nullptr 并在SomeMethod 调用之前检查它。

【讨论】:

  • g 存储指向存储的行指针以供进一步处理,存储关心在执行结束时删除其内容。我无法编辑 g 的实现。
【解决方案3】:

只需多一点代码,您就可以保留一个本地的、非拥有的视图:

void g(std::unique_ptr<Foo> p);     // takes ownership

int main()
{
    auto p = std::make_unique<Foo>();
    Foo & local_view = *p;

    if (/* condition */) { g(std::move(p)); }

    local_view.do_stuff();
}

【讨论】:

  • 确实,这是最清晰最好的解决方案。当您开始使用release() 时,所有权变得更加难以遵循,并且很容易意外破坏您使用unique_ptr 获得的异常安全性。
  • 感谢您首先定义智能指针然后从中创建本地视图的想法,这比定义原始指针然后创建 unique_ptr 包装器更正确。但我不想为单个实体使用多个变量 - 代码是开源的,f() 的实体很大,所有权在几个地方转移并且经常使用指针,解决方案似乎容易出错对我来说。
  • 显然这是最好的解决方案,除非存在所需的智能指针。谢谢!
猜你喜欢
  • 2016-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-23
相关资源
最近更新 更多