C++ 这个std::vector和std::shared_ptr内存泄漏是一个bug吗?
假设这个类C++ 这个std::vector和std::shared_ptr内存泄漏是一个bug吗?,c++,vector,memory-management,shared-ptr,allocation,C++,Vector,Memory Management,Shared Ptr,Allocation,假设这个类Foo: struct Foo { std::shared_ptr<int> data; std::shared_ptr<std::vector<Foo>> foos; }; 输出: use count: 9 这很好(1foo+8.foos)。 但是,当main()返回时,似乎仍然会有9个8指针指向.data!这可以通过将foo放入本地作用域并让一个额外的指针指向.data来演示,以观察该指针然后使用_count(): int m
Foo
:
struct Foo {
std::shared_ptr<int> data;
std::shared_ptr<std::vector<Foo>> foos;
};
输出:
use count: 9
这很好(1foo
+8.foos
)。
但是,当main()
返回时,似乎仍然会有9个8指针指向.data
!这可以通过将foo
放入本地作用域并让一个额外的指针指向.data
来演示,以观察该指针然后使用_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';
}
这不好。我会将使用count | after
作为1
,因为foo
及其所有成员都应该在范围结束时解构。嗯,foo
确实被解构了(否则use\u count |
之后将是10
而不是9
),但它的.foos
向量指针没有解构。而ptr
只是一个std::shared_ptr
,因此与struct Foo
毫无关系。所有这一切都可以通过提供struct Foo
一个析构函数来修复,该析构函数可以手动重置reset()
s.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';
}
但必须手动重置这些指针似乎很奇怪。为什么std::vector
或std::shared\u ptr
不在此处自动执行此操作?是虫子吗
我正在使用Visual Studio Community 2017版本15.9.5-感谢您的帮助 问题是您有一个循环引用 当
foo
被销毁时,它会减少其shared\u ptr
的引用计数,但这些计数不会达到零
所以,即使std::shared_ptr
是“不可访问的”,它仍然有指针。(注意:垃圾收集器使用“可访问性”来收集/释放指针)
打破循环的常用方法是使用
std::weak_ptr
问题是您有一个循环引用
当foo
被销毁时,它会减少其shared\u ptr
的引用计数,但这些计数不会达到零
所以,即使std::shared_ptr
是“不可访问的”,它仍然有指针。(注意:垃圾收集器使用“可访问性”来收集/释放指针)
打破这种循环的通常方法是使用
std::weak_ptr
您创建了一个循环依赖关系:每个Foo
包含所有其他Foo
的共享_ptr
s(即共享所有权)。这意味着任何Foo
都不会被破坏:要被破坏,use\u count
必须为零。但在进入析构函数之前,它不能为零,因为每隔一个Foo
仍然保存一个引用
这是共享所有权限制的经典案例——与某些信念相反,它不会自动解决所有所有权问题
我还想质疑每个
Foo
存储指向所有Foo
s的相同指针的意义。如果这就是你想要做的,它应该是静态的,但这听起来也不是一个好的设计。也许你可以详细说明你想要解决的实际问题(在一个新问题中)?你创建了一个循环依赖关系:每个Foo
包含所有其他Foo
的共享的ptr
s(即共享所有权)。这意味着任何Foo
都不会被破坏:要被破坏,use\u count
必须为零。但在进入析构函数之前,它不能为零,因为每隔一个Foo
仍然保存一个引用
这是共享所有权限制的经典案例——与某些信念相反,它不会自动解决所有所有权问题
我还想质疑每个
Foo
存储指向所有Foo
s的相同指针的意义。如果这就是你想要做的,它应该是静态的,但这听起来也不是一个好的设计。也许你可以详细说明你想要解决的实际问题(在一个新问题中)?不明白,在ent ptr仍然指向foos数据,使用计数为1?那么…什么问题?我明白了…对不起..当程序超出main()作用域时,指针将被释放。没有错误,这是预期的行为。您必须手动重置那些嵌套的Foo
s,因为您正在创建循环引用(Foo
s在向量中有一个共享的ptr到向量的in,从而使它们保持活动状态),而共享的ptr不是设计用来处理它的。Offtopic:disconstructed==destructed…不要获取它,在ent ptr仍然指向foos数据,使用计数为1?那么…什么问题?我明白了…对不起..当程序超出main()作用域时,指针将被释放。没有错误,这是预期的行为。您必须手动重置那些嵌套的Foo
s,因为您正在创建循环引用(向量中的Foo
s与向量中的共享ptr,从而使其保持活动状态)而shared_ptr并不是为处理它而设计的。Offtopic:disconstructed==destromed…也许要进一步澄清:foo.foos
的每个条目都是一个foo
的引用,它同时保留了foo.data
和foo.foos
。在这一点上,指向foo.foos
的所有指针都是可互换的,删除一个指针不会影响其余的指针。谢谢,这看起来就像我搜索的一样。我接受这个答案,尽管我还不知道如何在这个示例代码中使用weake\ptr
。它似乎没有任何类型的->
运算符。在使用相关的std::shared_ptr
(检查nullptr
)之前,必须先检查它。好的,我理解。但是由于弱ptr
希望设置为现有的共享ptr
,我必须将共享ptr
和弱ptr
都放在结构中,对吗?您不必同时放这两个。很难说正确的方式做你的山姆
use count | before: 0
use count | inside: 10
use count | after: 9
#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