使用std :: move与std :: shared_ptr

ksl*_*ksl 28 c++ shared-ptr move-semantics c++11

我有一个定义如下的函数:

void foo(std::shared_ptr<X> x) { ... };
Run Code Online (Sandbox Code Playgroud)

如果我将共享ptr声明为X:

std::shared_ptr<X> sourcePtr(new X(...));
Run Code Online (Sandbox Code Playgroud)

然后我可以打电话foo如下:

foo(std::move(sourcePtr));
Run Code Online (Sandbox Code Playgroud)

要么

foo(sourcePtr);
Run Code Online (Sandbox Code Playgroud)

我明白,如果我使用第一个选项,则sourcePtr变为null.它是否也会阻止引用计数递增?

如果这无关紧要,我更喜欢哪个选项?做出这样的决定时,我应该考虑其他事情吗?

Rei*_*ica 38

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

  1. 原件sourcePtr将变为null,并且

  2. 引用计数不会被修改.

如果您知道sourcePtr在函数调用之后将不再需要值,则将其移动到函数中是一种轻微的优化,因为它会保存原子增量(以及稍后减少,当sourcePtr超出范围时).

但是,请注意标识符sourcePtr对于作用域的其余部分仍然有效,只需保留空指针即可.这意味着如果你在移动后使用它,编译器不会抱怨,但是如果你忘了它被移动了,你很可能会取消引用null.我倾向于经常使用这种"优化" move,并且我也被它咬了几次:在函数中添加了更多的功能,如果你忘记撤消它move,你会得到一个很好的崩溃.

因此,当您不再需要它时,移动是轻微的优化,同时还有轻微的维护负担.在您的情况下,由您来衡量哪个更重要.

以上假设存在实际sourcePtr在其声明和最终调用之间使用的代码foo(感谢@WhozCraig指出它).如果没有,你会过创建在调用点的指针向右更好:

foo(std::make_shared<X>(...));
Run Code Online (Sandbox Code Playgroud)

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

  • 仅仅为了我自己的清晰度,如果创建和调用确实像示例一样简单(并且它可能不是),那么就不会`foo(std :: make_shared <X>(...));`完成同样的事情,没有放弃(并且正如你指出,潜在的风险)`std :: shared_ptr <X>`shell存在?当然,如果两者之间存在中间的`sourcePtr-> member()`代码(分配和调用),那么它不是一个选项.但如果没有? (5认同)
  • 我认为这毫无意义.这就是为什么引入`shared_ptr`的原因 - 不仅是因为安全性(避免内存泄漏),而且还因为(几乎)免费共享昂贵的复制对象.优化单个原子操作听起来像浪费时间. (3认同)