How to avoid memory leak with shared_ptr?

20,884

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, if main() holds a shared_ptr to A, which directly or indirectly holds a shared_ptr back to A, A's use count will be 2. Destruction of the original shared_ptr will leave A dangling with a use count of 1. Use weak_ptr to "break cycles."

Thanks, Glen, for the link.

Share:
20,884
Alexey Malistov
Author by

Alexey Malistov

Mathematician. In 2003 I graduated from the Moscow Institute of Physics and Technology.

Updated on November 18, 2020

Comments

  • Alexey Malistov
    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
    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
    curiousguy over 12 years
    Which one? And why not replace both with weak references? This is ridiculous. shared_ptr was used for a reason.
  • James McNellis
    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
    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
    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
    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
    t0rakka over 9 years
    The 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. :)