这是std :: vector和std :: shared_ptr内存泄漏的错误吗?

时间:2019-01-30 10:02:06

标签: c++ vector memory-management shared-ptr allocation

假设此类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-感谢您的帮助!

2 个答案:

答案 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,但这并不听起来不错的设计无论是。也许你可以详细你想的那么实际解决问题(在一个新的问题)?