31

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?

jww
  • 97,681
  • 90
  • 411
  • 885
Alexey Malistov
  • 26,407
  • 13
  • 68
  • 88

1 Answers1

61

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.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • 6
    @Alexey, here's a link to the docs where it explicitly warns of this problem in the introduction. http://www.boost.org/doc/libs/1_41_0/libs/smart_ptr/shared_ptr.htm – Glen Dec 01 '09 at 15:32
  • 2
    Which one? And why not replace both with weak references? This is ridiculous. `shared_ptr` was used for a reason. – curiousguy Oct 09 '11 at 02:10
  • 7
    @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. – James McNellis Oct 09 '11 at 04:54
  • @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. – curiousguy May 31 '12 at 22:46
  • 1
    @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. – t0rakka Nov 17 '14 at 14:58
  • @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? – curiousguy Nov 17 '14 at 20:54
  • 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. :) – t0rakka Nov 18 '14 at 13:10