smart_ptr到类segfault的属性

smart_ptr to attribute of class segfault

本文关键字:属性 segfault 到类 ptr smart      更新时间:2023-10-16

我想知道这个例子是否会导致segfault,因为对象的dtor被称为。我仍然持有对象属性的shared_ptr。

struct foo{
std::shared_ptr<std::string> const bar = std::make_shared<std::string>("foo");
foo() {std::cout << "CTOR!" << std::endl;} 
~foo(){std::cout << "DTOR!" << std::endl;}
};
int main() {
std::shared_ptr<std::string> ptr;
{
std::shared_ptr<foo> foo_ptr = std::make_shared<foo>();
ptr = foo_ptr->bar;
}
std::cout << *ptr << std::endl;

return 0;
}
不,不会的。通过将一个std::shared_ptr分配给另一个,你就是在否认它的死亡。

此操作ptr = foo_ptr->bar;将使共享指针的计数器增加一。这将保证空闲存储上动态分配的对象仍然有效。

即使是被破坏对象的属性也是如此吗

是的,这是真的。在非形式的谈话中,动态分配内存的用途之一是当你希望你的对象比它的所有者(另一个对象,指针…)活得更多时。


尝试执行此代码。它会让你明白:

std::shared_ptr<std::string> ptr;
{
std::shared_ptr<foo> foo_ptr = std::make_shared<foo>();
std::cout <<"References Count:" << foo_ptr->bar.use_count()<<"n";
ptr = foo_ptr->bar;
std::cout <<"References Count:" << foo_ptr->bar.use_count()<<"n";
}
std::cout <<"References Count:" << ptr.use_count()<<"n";
std::cout << *ptr << std::endl;

它将输出:

CTOR!
References Count:1
References Count:2
DTOR!
References Count:1
foo

在线演示

这里没有问题。std::shared_ptr正是为了做到这一点而设计的。如果我们看看代码是如何工作的,你可以看到

std::shared_ptr<std::string> ptr;

创建一个名为CCD_ 5的CCD_。

{
std::shared_ptr<foo> foo_ptr = std::make_shared<foo>();
ptr = foo_ptr->bar;
}

现在我们创建名为foo_ptrshared_ptr,它指向foo的有效实例,而foo的构造函数初始化bar。然后,我们将bar分配给ptr。当我们这样做时,bar的内部引用计数器增加1(到2),现在barptr共享内部指针。然后我们到达作用域的末尾,foo_ptr被销毁,当它被销毁时,它调用foo的析构函数,后者调用bar的析构因子。当bar被破坏时,它放弃对指针的访问。这意味着它递减参考计数器。如果它是最后一个shared_ptr(当输入析构函数时计数器为1),则指针也会被删除,如果不是,则不执行其他操作。由于计数器不止一个(由于ptr = foo_ptr->bar,指针为两个),指针不会被删除,而是保留在ptr中,因为所有权是共享的

std::cout << *ptr << std::endl;

这里ptr仍然有效,因为它共享所有权,所以没有问题。当程序结束时,我们进入ptr的析构函数,看到引用计数器为1,因此指针被删除,从而确保我们没有内存泄漏。因此,std::shared_ptr不仅为我们提供了指针的安全复制,而且还提供了自动内存管理。不过,这确实需要一些额外的费用。有一个原子引用计数器可以使用,所以它没有原始指针那么小或那么快,但它要安全得多。