【问题标题】:What are the benefits and risks, if any, of using std::move with std::shared_ptr将 std::move 与 std::shared_ptr 一起使用有哪些好处和风险(如果有)
【发布时间】:2014-05-23 10:00:18
【问题描述】:

我正在学习 C++11 功能,作为其中的一部分,我首先进入了 unique_ptrshared_ptr 的世界。

当我开始时,我编写了一些专门使用 unique_ptr 的代码,因此,当我传递我的变量时,我需要使用 std::move 来完成它(或者我是这样理解的)。

经过一番努力,我意识到我确实需要shared_ptr 而不是我正在做的事情。稍后快速查找/替换,我的指针被切换到共享,但我懒惰地只留下了 move() 调用。

令我惊讶的是,它不仅编译了,而且在我的程序中表现得非常好,我得到了我所期待的每一盎司功能......特别是,我能够将 shared_ptr 从 ObjectA 中“移动”到ObjectB,两个对象都可以访问它并且可以操纵它。太棒了。

这对我提出了一个问题……现在我在shared_ptr 上,move() 调用实际上是否在做任何事情?如果是这样,它是什么,它的后果是什么?

代码示例

shared_ptr<Label> lblLevel(new Label());

//levelTest is shared_ptr<Label> declared in the interface of my class, undefined to this point
levelTest = lblLevel;

//Configure my label with some redacted code

//Pass the label off to a container which stores the shared_ptr in an std::list
//That std::list is iterated through in the render phase, rendering text to screen
this->guiView.AddSubview(move(lblLevel));

此时,我可以对 levelTest 进行重要更改,例如更改文本,这些更改会反映在屏幕上。

这对我来说似乎levelTest 和列表中的shared_ptr 都是同一个指针,而move() 确实没有做太多事情。这是我的业余解释。寻找洞察力。在 Windows 上使用 MinGW。

【问题讨论】:

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


    【解决方案1】:

    ecatmur 的回答解释了为什么事情的行为与您在一般意义上看到的一样。

    具体到您的情况,levelTestlblTest副本,它创建了对共享资源的附加拥有引用。您从 lblTest 移出,因此 levelTest 完全不受影响,并且它对资源的所有权保持不变。

    如果您查看lblTest,我相信您会看到它被设置为空值。因为您在移动之前复制了shared_ptr,所以现有的两个指针(levelTestguiView 中的值)的活动实例都应该引用相同的底层指针(它们的get 方法返回相同的值)并且应该至少有两个引用(它们的 use_count 方法应该返回 2,如果您制作了额外的副本,则应该返回更多)。

    shared_ptr 的全部意义在于启用您所看到的内容,同时在所有shared_ptr 实例被破坏时仍允许自动清理资源。

    【讨论】:

    • 请注意,如果AddSubView 通过左值引用(或特别是 const-左值引用)获取其参数,这将撤消move() 的效果(毕竟,这只是一个cast) 和一个(可能是临时的)副本可能无论如何都会生成。在这种情况下,您可能仍然会发现 lblLevel 不为空。
    【解决方案2】:

    当您从可转换类型的共享指针移动构造或移动分配时,源指针变为空,根据 20.7.2.2.1:

    22 - 后置条件*this 应包含 r 的旧值。 r 应为r.get() == 0.

    因此,如果您观察到源指针在移动构造或移动赋值后仍然有效,那么您的编译器不正确或您使用 std::move 不正确。

    例如:

    std::shared_ptr<int> p = std::make_shared<int>(5);
    std::shared_ptr<int> q = std::move(p);
    assert(p.get() == nullptr);
    

    【讨论】:

    • 您好 ecatmur,我使用缩短的代码示例进行了编辑。我非常有信心在我当前的代码中也没有 nullptr。我希望这能提供清晰的信息。我想完全理解这一点,所以我不只是到处乱扔垃圾。
    • @Jasconius AddSubview 的签名是什么?
    • @Jasconius 好的,所以lblLevel 被移出,但这不会影响levelTest。这是意料之中的。
    • 不,几乎没有区别 - 通过使用 move,您节省了原子增量,但这是一个很小的成本。
    • @ecatmur “通过使用 move 你可以节省原子增量”和以后的减量......对于所有架构来说不一定是“微小的成本”——即使假设争用/旋转最小——因为它可以在其他一些线程/核心可以下一次修改计数器之前强制缓存同步。与互斥体相比,原子增量很便宜,但与许多其他东西相比 - 不是。
    【解决方案3】:

    如果您复制一个 shared_ptr,则对指针目标的引用计数会增加(以线程安全的方式)。

    相反,当您将一个 shared_ptr 从 A 移动到 B 时,B 包含移动前 A 的状态副本,而 A 为空。没有线程安全的引用计数递增/递减,但在 A 和 B 的内部位之间进行了一些非常简单且廉价的指针交换。

    您可以将移动视为一种从移动源到移动目的地的“窃取资源”的有效方式。

    【讨论】:

      猜你喜欢
      • 2015-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-28
      相关资源
      最近更新 更多