How to avoid memory leak with shared_ptr?
If you have circular references like this, one object should hold a weak_ptr
to the other, not a shared_ptr
.
From the shared_ptr
introduction:
Because the implementation uses reference counting, cycles of
shared_ptr
instances will not be reclaimed. For example, ifmain()
holds ashared_ptr
toA
, which directly or indirectly holds ashared_ptr
back toA
,A
's use count will be 2. Destruction of the originalshared_ptr
will leaveA
dangling with a use count of 1. Useweak_ptr
to "break cycles."
Thanks, Glen, for the link.
Alexey Malistov
Mathematician. In 2003 I graduated from the Moscow Institute of Physics and Technology.
Updated on November 18, 2020Comments
-
Alexey Malistov over 3 years
Consider the following code.
using boost::shared_ptr; struct B; struct A{ ~A() { std::cout << "~A" << std::endl; } shared_ptr<B> b; }; struct B { ~B() { std::cout << "~B" << std::endl; } shared_ptr<A> a; }; int main() { shared_ptr<A> a (new A); shared_ptr<B> b (new B); a->b = b; b->a = a; return 0; }
There is no output. No desctructor is called. Memory leak. I have always believed that the smart pointer helps avoid memory leaks.
What should I do if I need cross-references in the classes?
-
Glen over 14 years@Alexey, here's a link to the docs where it explicitly warns of this problem in the introduction. boost.org/doc/libs/1_41_0/libs/smart_ptr/shared_ptr.htm
-
curiousguy over 12 yearsWhich one? And why not replace both with weak references? This is ridiculous.
shared_ptr
was used for a reason. -
James McNellis over 12 years@curiousguy: I'm not sure I understand your question: what do you find to be ridiculous? To break a cycle, you must replace one strong reference with a weak reference; which one depends entirely on the use case. You can't replace all of the strong references with weak references, because then all of the objects will be destroyed as there are no owners left.
-
curiousguy almost 12 years@JamesMcNellis "what do you find to be ridiculous?" This idea that a reference can be replaced by a non-reference (called "weak" reference). This is insane.
-
t0rakka over 9 years@curiousguy It isn't insane at all. Consider when the shared_ptr a and b go out of scope, they both decrease their internal reference counts. The count never reaches zero, since the each still have count of 1 from each other. The destructor is NEVER called because the count never reaches zero. They are holding each other hostage. One or the other has to yield ownership; you choose which one with the weak reference. This works with even larger number of links in the chain. Just, one of the links in the chain has to be weak to break the cyclic dependency.
-
curiousguy over 9 years@SnappleLVR The proposed solution is insane. The smart ptr is working as needed, as the semantics of owning ptr implies that the referenced object isn't destroyed before the owner. IOW, the design implies the objects cannot be automatically destroyed. "one of the links in the chain has to be weak" which one?
-
t0rakka over 9 yearsThe original design implies that the objects cannot be automatically destroyed. The revised solution guarantees destruction of every object. The problem is that the weak pointer can now point to an object that does not exist. The example itself as toy code is working fine with these revisions but something more complicated is a completely different topic. I wouldn't program something like this but the answer is correct and easily provable. FYI: didn't know that pressing enter does not go to newline but posts. I was going to write more above, obviously. :)