C++ 使用std::move和std::shared\u ptr的好处和风险是什么

C++ 使用std::move和std::shared\u ptr的好处和风险是什么,c++,c++11,shared-ptr,move-semantics,unique-ptr,C++,C++11,Shared Ptr,Move Semantics,Unique Ptr,我正在学习C++11的特性,作为其中的一部分,我将率先进入独特的\u ptr和共享的\u ptr世界 当我开始时,我编写了一些专门使用unique\u ptr的代码,因此当我传递变量时,我需要使用std::move来完成这项工作(或者我是这样理解的) 经过一番努力,我意识到我真正需要的是共享\u ptr,而不是我正在做的事情。稍后快速查找/替换,我的指针切换到共享,但我只是懒洋洋地保留了move()调用 令我惊讶的是,它不仅编译了,而且在我的程序中表现得非常好,我得到了我所期望的每一点功能。。。

我正在学习C++11的特性,作为其中的一部分,我将率先进入
独特的\u ptr
共享的\u ptr
世界

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

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

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

这给我提出了一个问题。。。既然我在
shared\u 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));
shared_ptr lblLevel(新标签());
//levelTest在我的类的接口中声明为shared_ptr,目前尚未定义
levelTest=lblLevel;
//用一些修改过的代码配置我的标签
//将标签传递给一个容器,该容器将共享的ptr存储在std::list中
//该std::list在渲染阶段迭代,将文本渲染到屏幕
这->guiView.AddSubview(move(lblLevel));
在这一点上,我可以对levelTest进行重要的更改,比如更改文本,这些更改会反映在屏幕上


在我看来,似乎列表中的
levelTest
和shared\u ptr都是同一个指针,而且
move()
确实做得不多。这是我业余的口译。寻找洞察力。在Windows上使用MinGW

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

22-后置条件:
*此
应包含
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);
std::shared_ptr p=std::make_shared(5);
std::shared_ptr q=std::move(p);
断言(p.get()==nullptr);
如果复制共享\u ptr,指针目标的引用计数将增加(以线程安全的方式)

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


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

ecatmur的回答解释了从一般意义上看事情为何会发生这样的行为

特别是对于您的情况,
levelTest
lblTest
的一个副本,它创建了对共享资源的额外拥有引用。您从
lblTest
移动,因此
levelTest
完全不受影响,其对资源的所有权保持不变

如果您查看
lblTest
我相信您会看到它被设置为空值。由于您在移出
shared_ptr
之前复制了它,指针的现有活动实例(
levelTest
guiView
中的值)应该引用相同的底层指针(它们的
get
方法返回相同的值),并且应该至少有两个引用(他们的
use\u count
方法应返回
2
,如果您制作了其他副本,则返回更多)


shared\u ptr
的全部意义在于,当所有
shared\u ptr
实例都被破坏时,在允许自动清理资源的同时启用您看到的东西。

嗨,ecatmur,我用一个简短的代码示例进行了编辑。我很有信心当前代码中的nullptr也不是空的。我希望这能提供清晰的信息。我想完全理解这一点,所以我不只是到处乱扔垃圾。@Jasconius
AddSubview
的签名是什么?@Jasconius好的,所以
lblLevel
被移走了,但这不会影响
levelTest
。这是意料之中的。不,实际上没有区别-使用
move
您正在保存一个原子公司请记住,这是一个很小的成本。@ecatmur“通过使用移动,您可以节省一个原子增量”和一个随后的减量……不一定是一个“小成本”对于所有架构-即使假设最小的争用/旋转-给定它可以在其他线程/核心下一步修改计数器之前强制缓存同步。与互斥量相比,原子增量便宜,但与许多其他东西相比-不是。请注意,if
AddSubView
通过左值引用获取其参数(尤其是常量左值引用),这将撤消
move()
(毕竟,这只是一个强制转换)的效果,并且可能会生成一个(可能是临时的)副本。在这种情况下,您可能仍然会发现
lblLevel
是非空的。