smart_ptr 到 class 段错误的属性
smart_ptr to attribute of class 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;
创建一个名为 ptr
的 shared_ptr
,它没有任何指向。
{
std::shared_ptr<foo> foo_ptr = std::make_shared<foo>();
ptr = foo_ptr->bar;
}
现在我们创建名为 foo_ptr
的 shared_ptr
,它指向 foo
的有效实例并且 foo
的构造函数初始化 bar
。然后我们将 bar
分配给 ptr
。当我们这样做时,bar
的内部引用计数器增加一(到二),现在 bar
和 ptr
共享 内部指针。然后我们到达范围的末尾并且 foo_ptr
被销毁,当它被销毁时它调用 foo
的析构函数,它调用 bar
的析构函数。当 bar
被销毁时,它放弃了对指针的访问。这意味着它会递减引用计数器。如果它是最后一个 shared_ptr
(进入析构函数时计数器为 1),那么指针也将被删除,如果不是,那么它什么都不做。由于计数器不止一个(由于ptr = foo_ptr->bar
,它是两个)指针没有被删除,而是存在于ptr
中,因为所有权是共享.
std::cout << *ptr << std::endl;
这里 ptr
仍然有效,因为它共享所有权,所以没有问题。当程序结束时,我们进入 ptr
的析构函数并看到引用计数器为 1,因此指针被删除,从而确保我们没有内存泄漏。所以 std::shared_ptr
不仅为我们提供了指针的安全复制,还为我们提供了自动内存管理。不过,这确实会带来一些额外费用。有一个原子引用计数器可以使用,因此它不像原始指针那么小或那么快,但它更安全。
我想知道这个例子是否会导致段错误,因为对象的 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;
创建一个名为 ptr
的 shared_ptr
,它没有任何指向。
{
std::shared_ptr<foo> foo_ptr = std::make_shared<foo>();
ptr = foo_ptr->bar;
}
现在我们创建名为 foo_ptr
的 shared_ptr
,它指向 foo
的有效实例并且 foo
的构造函数初始化 bar
。然后我们将 bar
分配给 ptr
。当我们这样做时,bar
的内部引用计数器增加一(到二),现在 bar
和 ptr
共享 内部指针。然后我们到达范围的末尾并且 foo_ptr
被销毁,当它被销毁时它调用 foo
的析构函数,它调用 bar
的析构函数。当 bar
被销毁时,它放弃了对指针的访问。这意味着它会递减引用计数器。如果它是最后一个 shared_ptr
(进入析构函数时计数器为 1),那么指针也将被删除,如果不是,那么它什么都不做。由于计数器不止一个(由于ptr = foo_ptr->bar
,它是两个)指针没有被删除,而是存在于ptr
中,因为所有权是共享.
std::cout << *ptr << std::endl;
这里 ptr
仍然有效,因为它共享所有权,所以没有问题。当程序结束时,我们进入 ptr
的析构函数并看到引用计数器为 1,因此指针被删除,从而确保我们没有内存泄漏。所以 std::shared_ptr
不仅为我们提供了指针的安全复制,还为我们提供了自动内存管理。不过,这确实会带来一些额外费用。有一个原子引用计数器可以使用,因此它不像原始指针那么小或那么快,但它更安全。