为什么我会std ::移动一个std :: shared_ptr?
我一直在查看Clang的源代码 ,发现这个代码片段:
void CompilerInstance::setInvocation( std::shared_ptr<CompilerInvocation> Value) { Invocation = std::move(Value); }
为什么我要std::move
一个std::shared_ptr
?
在共享资源上转让所有权有没有意义?
为什么我不这样做呢?
void CompilerInstance::setInvocation( std::shared_ptr<CompilerInvocation> Value) { Invocation = Value; }
我认为其他答案没有强调的是速度 。
std::shared_ptr
引用计数是primefaces的 。 增加或减less参考计数需要primefaces增量或减量 。 这比非primefaces增量/减量要慢百倍,更不用说如果我们增加和减less同一个计数器,我们就得到确切的数字,浪费了大量的时间和资源。
通过移动shared_ptr
而不是复制它,我们“窃取”primefaces引用计数,并且使其他shared_ptr
无效。 “偷取”引用计数不是primefaces的,比复制shared_ptr
(和caterprimefaces引用的增量或减量)要快上百倍。
请注意,这种技术纯粹用于优化。 复制它(正如你所build议的)同样也是function强大的。
通过使用move
您避免增加,然后立即减less股份数量。 这可以为您节省一些昂贵的primefaces操作的使用计数。
对std::shared_ptr
移动操作(如移动构造函数)很便宜 ,因为它们基本上是“窃取指针” (从源到目标;更准确地说,整个状态控制块从源到目标“被盗”,包括引用计数信息)。
而是在std::shared_ptr
上复制操作,调用primefaces引用计数增加(即,不仅仅是在整数RefCount
数据成员上的++RefCount
,而是例如在Windows上调用InterlockedIncrement
),这比仅仅窃取指针/状态更昂贵 。
因此,详细分析这种情况下的参考计数dynamic:
// shared_ptr<CompilerInvocation> sp; compilerInstance.setInvocation(sp);
如果你通过值传递sp
,然后在CompilerInstance::setInvocation
方法中获取一个副本 ,你有:
- 当进入方法时,
shared_ptr
参数被复制构造:ref count atomic increment 。 - 在方法体内部, 将
shared_ptr
参数复制到数据成员中:ref count atomic increment 。 - 退出该方法时,
shared_ptr
参数被破坏:ref count atomic decrement 。
你有两个primefaces增量和一个primefaces减量,总共三个 primefaces操作。
相反,如果你通过value来传递shared_ptr
参数,然后std::move
在方法内(正如Clang的代码所做的那样),你有:
- 当进入方法时,
shared_ptr
参数被复制构造:ref count atomic increment 。 - 在方法体内部,
std::move
shared_ptr
参数std::move
到数据成员中:ref count不会改变! 你只是在窃取指针/状态:不涉及昂贵的primefaces引用计数操作。 - 退出该方法时,
shared_ptr
参数被破坏; 但是因为你在步骤2中移动了,所以没有什么可以破坏的,因为shared_ptr
参数没有指向任何东西。 同样,在这种情况下不会发生primefaces减量。
底线:在这种情况下,只有一个引用计数primefaces增量,即只有一个primefaces操作。
正如你所看到的,这比两个primefaces增量加上一个primefaces减量(总共三个primefaces操作)要好得多 。
复制shared_ptr
包括复制其内部状态对象指针并更改引用计数。 移动它只涉及交换指针到内部引用计数器和拥有的对象,所以它更快。
在这种情况下使用std :: move有两个原因。 大部分的回应都是针对速度问题,而忽略了更清楚地显示代码意图的重要问题。
对于std :: shared_ptr,std :: move明确表示指针对象所有权的转移,而简单的复制操作则添加了其他所有者。 当然,如果原来的所有者随后放弃其所有权(例如通过允许它们的std :: shared_ptr被销毁),则所有权的转移已经完成。
当你用std :: move转移所有权时,很明显发生了什么事情。 如果您使用普通副本,则直到您确认原始所有者立即放弃所有权后,才能确定所执行的操作并不明显。 作为奖励,更有效的实现是可能的,因为所有权的primefaces转移可以避免业主数量增加了一个临时状态(以及随之而来的参考数量的变化)。