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
这很好(1
foo
+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