我对由lambda中的值捕获的shared_ptr的寿命感到好奇。
我曾期望只要lambda仍在内存中,它的use_count()
总是> = 1,但是我的测试显示出一些意外:使用计数降为0,然后在lambda主体内部增加为1。 ..
这是我测试的内容:
在第3阶段,shared_ptr的use_count()
降为0-但是对象没有被销毁。在第4阶段-Lambda内-use_count()
返回到1。运行Lambda之后,use_count()
回到0,但是直到销毁Lambda为止,对象才被销毁。
我想知道为什么/为什么会这样?
use_count()
是否应该在lambda定义之后为2,然后在lambda内部为1?
#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 ---
答案 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