【问题标题】:Using std::move with std::shared_ptr将 std::move 与 std::shared_ptr 一起使用
【发布时间】:2015-06-21 01:04:35
【问题描述】:

我有一个函数定义如下:

void foo(std::shared_ptr<X> x) { ... };

如果我向X声明一个共享ptr:

std::shared_ptr<X> sourcePtr(new X(...));

然后我可以拨打foo如下:

foo(std::move(sourcePtr));

foo(sourcePtr);

我知道如果我使用第一个选项,那么sourcePtr 将变为空。它是否也阻止了引用计数的增加?

如果没关系,我应该更喜欢哪个选项?在做出这样的决定时,我是否应该考虑其他任何事情?

【问题讨论】:

  • 简而言之,您是对的,参考计数器不会增加。但是我看不到它的实际用途,除非你想强制你传递的指针应该被清空,在这种情况下你可以使用std::unique_ptr。您不应该真正将新的智能指针视为 pointer,而应该从 资源所有权 的角度来看待它们。资源可以仅由单个实体 (std::unique_ptr) 或由多个实体 (std::shared_ptr) 拥有。
  • 这是一个非常相似的问题,虽然我不确定是否完全重复:stackoverflow.com/questions/10953325/…。这个问题是关于将参数传递给函数 - 这种方式不同......
  • @JoachimPileborg 这是一些测试代码的简化示例,它并不能说明整个故事,但可以让我将问题专门集中在我想知道的内容上。 Foo 需要在我的应用程序中使用shared_ptr,但在调用它之后测试代码不需要sourcePtr

标签: c++ c++11 shared-ptr move-semantics


【解决方案1】:

是的,如果将共享指针移动到函数中,那么:

  1. 原来的sourcePtr将变为空,并且

  2. 引用计数未被修改。

如果您知道在函数调用后您将不再需要 sourcePtr 的值,将其移动到函数中是一个轻微的优化,因为它节省了 原子 增量(以及以后的减量) , 当sourcePtr 超出范围时)。

但是,请注意标识符sourcePtr 在范围的其余部分仍然有效,只是它包含一个空指针。这意味着如果您在移动后使用它,编译器不会抱怨,但如果您忘记它已被移动,您很可能会取消引用 null。我倾向于经常使用这个“优化”move,我也被它咬过几次:函数中添加了更多功能,如果你忘记撤消move,你会得到一个不错的崩溃。

因此,当您不再需要它时进行移动只是轻微的优化和轻微的维护负担。您可以自行权衡哪个对您的情况更重要。

以上假设在其声明和对foo 的最终调用之间存在实际使用sourcePtr 的代码(感谢@WhozCraig 指出)。如果没有, 最好在调用站点直接创建指针:

foo(std::make_shared<X>(...));

这样,您可以保存相同数量的原子操作,并且您不会有潜在危险的空共享指针。

【讨论】:

  • 我认为这毫无意义。这就是引入shared_ptr 的原因——不仅是因为安全(在避免内存泄漏方面),还因为可以(几乎)免费安全地共享复制成本高的对象。优化单个原子操作听起来像是在浪费时间。
  • 只是为了我自己的清楚,如果创建和调用确实像示例一样简单(可能不是),那么foo(std::make_shared&lt;X&gt;(...)); 不会不遗余力地完成同样的事情(正如你所指出的,潜在的风险)std::shared_ptr&lt;X&gt; shell 到处乱跑?如果两者之间(分配和调用)之间存在中间sourcePtr-&gt;member() 代码,那么它当然不是一个选项。但如果没有呢?
  • 虽然我同意其他人的观点,这种优化违背了shared_ptr 的目的,但这是我发现的第一篇讨论移动与复制指针的可维护性问题的帖子 (+1) .在许多情况下,您不希望到处引用相同的资源,原因与您不希望使用全局变量相同。如果你有动态绑定,你仍然需要指针,特别是unique_ptr。然后你必须使用move(更多代码),但你可以更好地控制你的资源并获得轻微的性能提升。
猜你喜欢
  • 1970-01-01
  • 2014-05-23
  • 1970-01-01
  • 2020-11-04
  • 2021-08-28
  • 2017-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多