std::vector 和 std::shared_ptr 内存泄漏是一个错误吗?

Is this std::vector and std::shared_ptr memory leakage a bug?

假设这个 class Foo:

struct Foo {
    std::shared_ptr<int> data;
    std::shared_ptr<std::vector<Foo>> foos;
};

让我们创建一个 Foo 的实例,并在向 .foos 添加一些实例后查看其 .data 成员变量的 use_count():

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()returns的时候,还是会有98个指针指向.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::vectorstd::shared_ptr 不自动执行此操作?这是一个错误吗?


我正在使用 Visual Studio Community 2017 版本 15.9.5 - 感谢您的帮助!

问题是你有一个循环引用。

foo 被销毁时,它会减少其 shared_ptr 引用计数 ,但不会达到零。

所以即使 std::shared_ptr<std::vector<Foo>> 是 "inaccessible" ,上面仍然有指针。 (注意:垃圾收集器使用 "accessibility" 到 collect/free 指针)。

通常打破循环的方法是使用std::weak_ptr.

您创建了一个循环依赖:每个 Foo 包含所有其他 Fooshared_ptr(即股份所有权)。这意味着 Foo 永远不会被破坏:要被破坏,use_count 必须为零。但是在进入析构函数之前它不能为零,因为每隔 Foo 仍然持有一个引用。

这是共享所有权限制的典型案例 - 与某些信念相反,它不会自动解决您所有的所有权问题。

我还会质疑每个 Foo 存储指向所有 Foo 的相同指针的意义。如果那是你想要做的,它应该只是 static,但这听起来也不是好的设计。也许你可以详细说明你想要解决的实际问题(在一个新问题中)?