使用 std::move 和 std::shared_ptr 有什么好处和风险(如果有的话)
What are the benefits and risks, if any, of using std::move with std::shared_ptr
我正在学习C++11功能,作为其中的一部分,我首先潜入unique_ptr
和shared_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的回答解释了为什么事物的行为与你所看到的一般意义上一样。
具体到您的情况,levelTest
是lblTest
的副本,它创建对共享资源的额外拥有引用。您从lblTest
移动levelTest
因此完全不受影响,并且其对资源的所有权保持不变。
如果您查看lblTest
我相信您会看到它已设置为空值。因为您在从shared_ptr
移出之前创建了该的副本,因此指针的两个现有活动实例(levelTest
和 guiView
中的值(都应引用相同的基础指针(其 get
方法返回相同的值(,并且至少应有两个引用(其use_count
方法应返回 2
, 如果您制作了额外的副本,则更多(。
shared_ptr
的全部意义在于启用您所看到的功能,同时在所有shared_ptr
实例被破坏时仍允许自动清理资源。
当您从可转换类型的共享指针移动构造或移动赋值时,源指针变为空,根据 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);
如果复制shared_ptr,则对指针目标的引用计数将递增(以线程安全的方式(。
相反,当您将shared_ptr从 A 移动到 B 时,B 包含移动前 A 的状态副本,而 A 为空。没有线程安全的引用计数递增/递减,而是在 A 和 B 的内部位之间进行一些非常简单且廉价的指针交换。
您可以将移动视为从移动源到移动目的地"窃取资源">的有效方式。
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- 如果我std::dynamic_pointer_cast并且底层dynamic_cast的结果为null,那么返回的sh
- 如果 std::vector::clear() 不是静态的,如何在没有实例的情况下调用它?
- C++如果必须在编译时确定大小,std::array 有什么意义?
- 如果 KEY 是 std::list 或 std::vector 而不是值,那么 std::map 的默认行为是什么?
- 如果我重新定义 sqrt 函数,为什么使用 std::sqrt 失败?
- 如果模板没有可变参数,则 Lambda 被推导出为 std::function
- VSCode 说 std::chrono 是模棱两可的,如果运算符<<重载
- 如果在 2 个线程中使用,是否值得将size_t声明为 std::atomic?
- std::unordered_map 如果输入大小已知,如何优化批量插入
- 如果 lambda 没有指定的类型,std::function 如何接受 lambda?
- 如果只有 std::auto_ptr 可用,我是否仍应该使用智能指针?
- 为什么使用 std::vector 的代码不能编译,但使用 std::unique_ptr 如果没有 noexcept
- 使用用户定义的类型 UDT 实例化 std::atomic<>。如果 UDT 具有虚函数,则 l 墨水将失败。为什么?
- 如果我们不从 std::qsort 返回 0 会发生什么?
- 如果 std::function 捕获了unique_ptr,如何复制它?
- 使用 std::istream_iterator 时,它似乎跳过了空文件行 - 如果可能的话,如何避免这种情况?
- 如果迭代器没有因插入而无效,则使用std::find和C::insert()是线程安全的
- 如果真的需要std::move,我们应该什么时候声明右值refs
- 如果使用lambda,std::unique_ptr如何没有大小开销