使用 std::move 和 std::shared_ptr 有什么好处和风险(如果有的话)

What are the benefits and risks, if any, of using std::move with std::shared_ptr

本文关键字:std 如果 什么 shared move ptr 使用      更新时间:2023-10-16

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

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

经过一番努力,我意识到我真的需要shared_ptr而不是我正在做的事情。稍后快速查找/替换,我的指针已切换到共享,但我懒洋洋地将move()电话留了下来。

令我惊讶的是,这不仅编译了,而且在我的程序中表现得非常好,我得到了我所期望的每一盎司功能......特别是,我能够将shared_ptr从对象A"移动"到对象B,并且两个对象都可以访问它并可以操纵它。匪夷所思。

不过,这向我提出了一个问题...现在我在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。

ecatmur的回答解释了为什么事物的行为与你所看到的一般意义上一样。

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

如果您查看lblTest我相信您会看到它已设置为空值。因为您在从shared_ptr移出之前创建了该的副本,因此指针的两个现有活动实例(levelTestguiView 中的值(都应引用相同的基础指针(其 get 方法返回相同的值(,并且至少应有两个引用(其use_count方法应返回 2 , 如果您制作了额外的副本,则更多(。

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

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

22 - 后置条件*this应包含旧值 rr为空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);

如果复制shared_ptr,则对指针目标的引用计数将递增(以线程安全的方式(。

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

您可以将移动视为从移动

源到移动目的地"窃取资源">的有效方式。

相关文章: