按值捕获shared_ptr的lambda如何影响其use_count()?

时间:2019-05-23 16:40:59

标签: c++ c++11 lambda shared-ptr

我对由lambda中的值捕获的shared_ptr的寿命感到好奇。

我曾期望只要lambda仍在内存中,它的use_count()总是> = 1,但是我的测试显示出一些意外:使用计数降为0,然后在lambda主体内部增加为1。 ..

这是我测试的内容:

  1. 创建一个shared_ptr
  2. 定义一个按值捕获shared_ptr的lambda
  3. 重置shared_ptr
  4. 运行lambda

在第3阶段,shared_ptr的use_count()降为0-但是对象没有被销毁。在第4阶段-Lambda内-use_count()返回到1。运行Lambda之后,use_count()回到0,但是直到销毁Lambda为止,对象才被销毁。

我想知道为什么/为什么会这样?

use_count()是否应该在lambda定义之后为2,然后在lambda内部为1?


testing code on Repl.it:

#include <iostream>
#include <memory>

class Foo {
public:
  Foo( int v = 0 ) : val(v) {}
  ~Foo(){
    std::cout << "--- Foo destroyed ---" << std::endl;
  }
  int val = 0;
};

void logPtr( const std::shared_ptr<Foo>& p ){
    std::cout << "ptr: refs = " << p.use_count();
    if (p) {
      std::cout << ", val = " << p->val << std::endl;
    }
    else {
     std::cout << ", nullptr" << std::endl;
    }
}

int main() {

  std::shared_ptr<Foo> ptr = std::make_shared<Foo>( 0 );

  logPtr(ptr);

  std::cout << "--- define lambda ---\n";

  auto lambda = [=]() {

    std::cout << "--- run lambda ---\n";
    if (ptr) { ptr->val++; }
    logPtr(ptr);
    std::cout << "--- end lambda ---\n";

  };

  logPtr(ptr);

  std::cout << "--- reset ptr ---\n";
  ptr.reset();
  logPtr(ptr);

  // run lambda
  lambda();
  logPtr(ptr);

}

这是输出:

ptr: refs = 1, val = 0
--- define lambda ---
ptr: refs = 2, val = 0
--- reset ptr ---
ptr: refs = 0, nullptr
--- run lambda ---
ptr: refs = 1, val = 1
--- end lambda ---
ptr: refs = 0, nullptr
--- Foo destroyed ---

1 个答案:

答案 0 :(得分:1)

  

在lambda定义之后use_count()不应为2

是:

--- define lambda ---
ptr: refs = 2, val = 0
  

然后在lambda中放1个?

是:

--- run lambda ---
ptr: refs = 1, val = 1

您困惑的部分与lambdas无关。您可以通过创建共享指针的简单副本来产生相同的效果:

  std::shared_ptr<Foo> ptr = std::make_shared<Foo>( 0 );

  logPtr( ptr );

  std::cout << "--- define lambda ---\n";

  auto cpy = ptr;

  logPtr(ptr);

  std::cout << "--- reset ptr ---\n";    
  ptr.reset();

  logPtr(ptr);

  // run "lambda"   
  {
    std::cout << "--- run lambda ---\n";
    if (cpy) {
      cpy->val++;
    }
    logPtr( cpy );
    std::cout << "--- end lambda ---\n";    
  }

  logPtr( ptr );

您似乎缺少的是reset()的语义。作为cppreference explains,它

  

释放托管对象的所有权(如果有)。

什么意思

  

如果*this已经拥有一个对象,并且它是拥有它的最后一个shared_ptr ,则该对象将通过拥有的删除器销毁。

在您的代码中,最初有两个共享的指针,共享被引用对象的所有权。

ptr.reset()之后,第一个指针不再是所有者。它被重置回null / 0。但是,第二个指针(lambda内部的副本)仍然是所有者,并且使引用的对象保持活动状态(现在use_count为1)。

代码的其余部分仅检查两个不同的指针:一个仍然拥有该对象,另一个不拥有任何东西。

使用原始指针的等效代码如下:

Foo *ptr = new Foo(0);
Foo *cpy = ptr;  // create a copy
ptr = null;      // "reset" the first pointer
logPtr(cpy);     // examine the copy
delete cpy;      // release the object through the last active pointer