假设此类Foo
:
struct Foo {
std::shared_ptr<int> data;
std::shared_ptr<std::vector<Foo>> foos;
};
它有一个指向int的指针
它有一个指向该程序中将存在的所有实例的指针(因此这些实例之一== {*this
)
让我们创建Foo
的实例,并在向use_count()
添加了一些实例之后,查看其.data
成员变量的.foos
:
int main() {
Foo foo;
foo.data = std::make_shared<int>(5);
foo.foos = std::make_shared<std::vector<Foo>>();
foo.foos->resize(8);
for (auto & f : *foo.foos) {
f.data = foo.data;
f.foos = foo.foos;
}
std::cout << "use count: " << foo.data.use_count() << '\n';
}
输出:
use count: 9
哪个很好(1 foo
+ 8 .foos
)。
但是,似乎当main()
返回时,仍然会有 9 8个指向.data
的指针!可以通过将foo
放入本地范围并让另外一个指针指向.data
来观察此指针use_count()
来证明这一点:
int main() {
std::shared_ptr<int> ptr;
std::cout << "use count | before: " << ptr.use_count() << '\n';
{ //begin scope
Foo foo;
foo.data = std::make_shared<int>(5);
foo.foos = std::make_shared<std::vector<Foo>>();
foo.foos->resize(8);
for (auto & f : *foo.foos) {
f.data = foo.data;
f.foos = foo.foos;
}
ptr = foo.data;
std::cout << "use count | inside: " << ptr.use_count() << '\n';
} //end scope
std::cout << "use count | after: " << ptr.use_count() << '\n';
}
输出为:
use count | before: 0
use count | inside: 10
use count | after: 9
哪个不好。我希望use count | after
会成为1
,因为foo
及其所有成员都应在作用域末尾解构。好吧,foo
确实被解构了(否则use_count | after
将是10
而不是9
),但是它的.foos
向量指针并未被解构。 ptr
只是std::shared_ptr<int>
,因此与struct Foo
完全无关。所有这些都可以通过为struct Foo
手动提供reset()
指针的析构函数来解决:
.foos->data
产生更好的输出:
#include <memory>
#include <iostream>
#include <vector>
struct Foo {
~Foo() {
for (auto& p : *foos) {
p.data.reset();
}
}
std::shared_ptr<int> data;
std::shared_ptr<std::vector<Foo>> foos;
};
int main() {
std::shared_ptr<int> ptr;
std::cout << "use count | before: " << ptr.use_count() << '\n';
{
Foo foo;
foo.data = std::make_shared<int>(5);
foo.foos = std::make_shared<std::vector<Foo>>();
foo.foos->resize(8);
for (auto & f : *foo.foos) {
f.data = foo.data;
f.foos = foo.foos;
}
ptr = foo.data;
std::cout << "use count | inside: " << ptr.use_count() << '\n';
}
std::cout << "use count | after: " << ptr.use_count() << '\n';
}
但是似乎不得不手动重置这些指针很奇怪。为什么use count | before: 0
use count | inside: 10
use count | after: 1
或std::vector
在这里不自动这样做?是虫子吗?
我正在使用Visual Studio Community 2017版本15.9.5-感谢您的帮助!
答案 0 :(得分:6)
问题是您有一个循环引用。
foo
被销毁时,它会降低其shared_ptr
的引用数量,但不会达到零。
因此,即使std::shared_ptr<std::vector<Foo>>
是“不可访问的”,它上仍然有指针。 (注意:垃圾收集器使用“可访问性”来收集/释放指针)。
打破周期的通常方法是使用std::weak_ptr
。
答案 1 :(得分:1)
您创建了一个循环依赖项:每个Foo
包含所有其他shared_ptr
个元素中的Foo
个元素(即所有权)。这意味着,没有Foo
将永远得到破坏:被破坏,在use_count
必须是零。但它不能进入析构函数,因为每隔Foo
仍持有引用之前是零。
这是共享所有权限制的经典案例-与某些信念相反,它不能自动解决所有所有权问题。
我还要询问每个点Foo
存储相同指针所有Foo
S上。如果这是你想要做什么,这应该只是static
,但这并不听起来不错的设计无论是。也许你可以详细你想的那么实际解决问题(在一个新的问题)?