Assume this class Foo
:
struct Foo {
std::shared_ptr<int> data;
std::shared_ptr<std::vector<Foo>> foos;
};
it has a pointer to an int
it has a pointer to all instances that will exist in this program (therefore one of these instances == *this
)
Let's create a instance of Foo
and take a look at the use_count()
of its .data
member variable after having added some instances to .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';
}
output:
use count: 9
Which is fine (1 foo
+ 8 .foos
). However, it seem that when main()
returns, there will still be
9
8 pointers pointing to .data
! This can be demonstrated by putting foo
into a local scope and letting one additional pointer point to .data
to observe this pointers use_count()
afterwards:
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';
}
The output is:
use count | before: 0
use count | inside: 10
use count | after: 9
Which is not good. I would excpect use count | after
use count | after
to be 1
since foo
and all its members should get deconstructed at the end of the scope. Well, foo
definetely got deconstructed (otherwise use_count | after
would be 10
and not 9
) but its .foos
vector pointer weren't deconstructed. And ptr
is just a std::shared_ptr<int>
and therefore has nothing to do with struct Foo
at all. All this can be fixed by providing struct Foo
a destructor which reset()
s the .foos->data
pointer manually:
#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';
}
producing the nicer output:
use count | before: 0
use count | inside: 10
use count | after: 1
But it seem weird that one has to manually reset these pointers. Why do std::vector
or std::shared_ptr
not do that automatically here? Is it a bug?
I am using Visual Studio Community 2017 Version 15.9.5 - Thanks for any help!
Problem is that you have a circular reference.
When foo
is destroyed, it decreases reference count of its shared_ptr
, but those don't reach zero.
So even if std::shared_ptr<std::vector<Foo>>
is "inaccessible" , there are still pointer on it. (Note: Garbage collector uses "accessibility" to collect/free the pointers).
Usual method to break the cycle is to use std::weak_ptr
.
You created a circular dependency: Each Foo
contains shared_ptr
s (ie shares ownership) of all other Foo
s. This means no Foo
will ever get destructed: To be destroyed, the use_count
would have to be zero. But it cannot be zero before entering the destructor because every other Foo
still holds a reference.
This is a classic case of the limits of shared ownership - contrary to some beliefs, it does not automagically solve all your ownership problems.
I would also question the point of each Foo
storing the same pointer to all Foo
s. If that's what you want to do, it should just be static
, but that doesn't sound like good design either. Maybe you could detail the actual problem you want so solve (in a new question)?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.