Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/142.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么我要移动std::shared\u ptr?_C++_C++11_Shared Ptr_Smart Pointers_Move Semantics - Fatal编程技术网

C++ 为什么我要移动std::shared\u ptr?

C++ 为什么我要移动std::shared\u ptr?,c++,c++11,shared-ptr,smart-pointers,move-semantics,C++,C++11,Shared Ptr,Smart Pointers,Move Semantics,我一直在浏览,发现了以下片段: void CompilerInstance::setInvocation( std::shared_ptr<CompilerInvocation> Value) { Invocation = std::move(Value); } 通过使用move可以避免增加然后立即减少股票数量。这可能会在使用计数上为您节省一些昂贵的原子操作 复制共享\u ptr涉及复制其内部状态对象指针并更改引用计数。移动它只需要交换指向内部引用计数器和所属对象的指针

我一直在浏览,发现了以下片段:

void CompilerInstance::setInvocation(
    std::shared_ptr<CompilerInvocation> Value) {
  Invocation = std::move(Value);
}

通过使用
move
可以避免增加然后立即减少股票数量。这可能会在使用计数上为您节省一些昂贵的原子操作

复制
共享\u ptr
涉及复制其内部状态对象指针并更改引用计数。移动它只需要交换指向内部引用计数器和所属对象的指针,因此速度更快。
std::shared_ptr
的移动操作(如移动构造函数)很便宜,因为它们基本上是“偷指针”(从源到目标;更准确地说,整个状态控制块都是“偷来的”从源到目标,包括引用计数信息)

相反,对
std::shared_ptr
调用原子引用计数增加的复制操作(即不仅对整数
RefCount
数据成员执行
+RefCount
,而且在Windows上调用
interlockedCrement
),这比仅仅窃取指针/状态更昂贵

因此,详细分析该案例的参考计数动态:

// shared_ptr<CompilerInvocation> sp;
compilerInstance.setInvocation(sp);
//共享\u ptr sp;
compilerInstance.setInvocation(sp);
如果您按值传递sp,然后在
CompilerInstance::setInvocation
方法中进行复制,则您有:

  • 进入方法时,
    shared_ptr
    参数为copy-constructed:ref-count-atomicincrement
  • 在方法体内部,将
    共享的ptr
    参数复制到数据成员:ref count atomicincrement
  • 退出该方法时,
    shared_ptr
    参数被销毁:ref count atomic递减
  • 您有两个原子增量和一个原子减量,总共有三个原子操作

    相反,如果您通过值传递
    shared_ptr
    参数,然后在方法内部传递
    std::move
    (在Clang的代码中正确完成),则您有:

  • 进入方法时,
    shared_ptr
    参数为copy-constructed:ref-count-atomicincrement
  • 在方法主体内,将
    std::将
    shared_ptr
    参数移动到数据成员中:ref count不会更改!您只是在窃取指针/状态:不涉及昂贵的原子引用计数操作
  • 退出该方法时,
    shared_ptr
    参数被销毁;但是,由于您在步骤2中移动了,所以没有什么可破坏的,因为
    shared\u ptr
    参数不再指向任何东西。同样,在这种情况下不会发生原子衰减
  • 底线:在这种情况下,您只得到一个ref count原子增量,即只一个原子操作。

    如您所见,对于复制情况,这比两个原子增量加一个原子减量(总共三个原子操作)要好得多。

    我认为其他答案没有充分强调的一点是速度

    std::shared_ptr
    引用计数是原子的。增加或减少参考计数需要原子增量或减量。这比非原子递增/递减慢一百倍,更不用说如果我们递增或递减同一个计数器,我们得到的是精确的数字,在这个过程中浪费了大量的时间和资源

    通过移动
    共享的\u ptr
    而不是复制它,我们“窃取”原子引用计数,并使另一个
    共享的\u ptr
    无效。“窃取”引用计数不是原子的,它比复制
    共享\u ptr
    快一百倍(并导致原子引用增量或减量)


    请注意,此技术纯粹用于优化。复制它(正如您所建议的那样)在功能方面也很好。

    在这种情况下使用std::move有两个原因。大多数回答都提到了速度问题,但忽略了更清楚地显示代码意图这一重要问题

    对于std::shared_ptr,std::move明确表示指针对象的所有权转移,而简单的复制操作会添加额外的所有者。当然,如果原始所有者随后放弃其所有权(例如允许销毁其std::shared_ptr),则所有权转移已经完成


    当您使用std::move转移所有权时,很明显发生了什么。如果使用普通副本,除非验证原始所有者立即放弃所有权,否则无法确定预期操作是否为传输。作为奖励,更高效的实现是可能的,因为原子所有权转移可以避免所有者数量增加1(以及随之而来的引用计数变化)的临时状态.

    至少使用libstdc++时,移动和赋值的性能应该相同,因为
    操作符=
    调用传入指针上的
    std::move
    。请参阅:

    另一个值得注意的是:为什么它们不直接通过const引用传递,并避免整个std::move内容?因为“按值传递”还允许您直接传入原始指针,并且将只创建一个共享\u ptr。@Joseph,因为您无法移动常量reference@JosephIreland因为如果您将其称为
    compilerInstance.setInvocation(std::move(sp))则不会有增量。您可以通过添加一个重载来获得相同的行为,该重载需要一个
    共享\u ptr&&
    ,但为什么在不需要复制的时候复制呢?@Brunofereira我在回答我自己的问题。您不需要移动它,因为它是引用,只需复制它即可。仍然只有一份而不是两份。他们不这样做的原因是因为它会不必要地复制新构建的共享资源,例如。
    // shared_ptr<CompilerInvocation> sp;
    compilerInstance.setInvocation(sp);