7

I'm currently putting together an application that relies heavily on shared_ptr and everything looks good so far - I've done my homework and have a pretty good idea of some of the pitfalls of using shared_ptrs.

One of the most recognised problems with shared_ptr is cyclic dependencies - these issues can be solved by storing weak_ptrs that don't affect the lifetime of objects up the chain. However, I'm struggling to get my head around times where it's necessary to store a pointer to an external object via a weak_ptr - I'm not sure whether it's forbidden, discouraged, or whether it's safe.

The following diagram describes what I mean (black arrows indicate shared_ptr; dashed indicate weak_ptr):

alt text http://img694.imageshack.us/img694/6628/sharedweakptr.png

  • A parent contains shared_ptrs to two children, both of which point back to the parent using a weak_ptr.
  • In the constructor of the first child I retrieve via the parent weak_ptr the pointer to the second child and store it locally.

The code looks like this:

#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/enable_shared_from_this.hpp>

class child;
class child2;
class parent;

class parent : public boost::enable_shared_from_this<parent>
{
public:
    void createChildren()
    {
        _child2 = boost::make_shared<child2>(shared_from_this());
        _child = boost::make_shared<child>(shared_from_this());
    }

    boost::shared_ptr<child> _child;
    boost::shared_ptr<child2> _child2;
};

class child
{
public:
    child(boost::weak_ptr<parent> p)
    {
        _parent = p;
        _child2 = boost::shared_ptr<parent>(p)->_child2; // is this safe?
    }

    boost::weak_ptr<parent> _parent;
    boost::shared_ptr<child2> _child2;
};

class child2
{
public:
    child2(boost::weak_ptr<parent> p)
    {
        this->_parent = p;
    }

    boost::weak_ptr<parent> _parent;
};

int main()
{
    boost::shared_ptr<parent> master(boost::make_shared<parent>());
    master->createChildren();
}

I've tested this and it seems to work ok (I don't get any reports of memory leaks), however my question is: Is this safe? And if not, why not?

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Alan
  • 13,510
  • 9
  • 44
  • 50
  • Possible duplicate: http://stackoverflow.com/questions/1826902/how-to-avoid-memory-leak-with-boostsharedptr – Kirill V. Lyadvinsky Jan 26 '10 at 13:18
  • Not a duplicate. I'm using a weak_ptr to break the cyclic reference between parent and child. My question is regarding storing a shared_ptr to a sibling, retrieved via the parent. – Alan Jan 26 '10 at 15:22
  • 2
    Relying heavily on shared_ptr is not a good idea IMHO. This is a specialized tool, with certain use-cases in mind. In your example why not make the children scoped_ptrs, and the handle-to-parent a raw pointer? – rpg Jan 27 '10 at 11:17

3 Answers3

6

The child constructor appears to be safe the way you are calling it. However its not safe in general.

The issue is due to passing in a weak_ptr as the argument in the child constructor. This means you need to worry about whether the weak pointer is for an object that no-longer exists. By changing this parameter to a shared_ptrs and converting to a weak_ptr when storing we know that the object still exists. Here's the change:

child(boost::shared_ptr<parent> p)
{
    _parent = p;
    _child2 = p->_child2; // This is this safe
}
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
3

One of the most recognised problems with shared_ptr is cyclic dependencies - these issues can be solved by storing weak_ptrs that don't affect the lifetime of objects up the chain.

Wrong. This cyclic dependency exists or it does not.

If the issue exists, then weak reference is simply not an option.

where it's necessary to store a pointer to an external object via a weak_ptr

weak_ptr is almost never needed.

There are few quite specific cases where weak_ptr is appropriate, but mostly it's part of the shared_ptr cult: instead of randomly throwing shared_ptr at problems, they randomly throw half shared_ptr and half weak_ptr (as seen on SO).

curiousguy
  • 8,038
  • 2
  • 40
  • 58
  • If you have a cyclic dependency, changing a link to a weak pointer changes the nature of the dependency, possibly removing the problem. So it certainly is an option, even if there are others. – Dennis Zickefoose Oct 09 '11 at 03:16
  • 1
    @DennisZickefoose If you have a cyclic dependency, you cannot use weak references. If you can use weak references, it proves that you really don't have a cyclic **dependency**: since you can cope with the fact that one link can be broken, it isn't a dependency. You don't depend on something if you don't need it. "_changing a link to a weak pointer changes the nature of the dependency_" Yes, it changes the nature of the dependency to: not a dependency. It helps. – curiousguy Oct 09 '11 at 03:25
  • I've seen your rants on a few questions regarding shared pointers and I am somewhat in agreeance. weak_ptr helps with a few cases, but doesn't solve the general problem. Are you still of this mind? – Matt Joiner May 06 '12 at 13:43
  • @MattJoiner Yes. "Fixing" cyclic dependency by replacing a normal reference (aka strong reference) with a "weak reference" (aka _not-a reference-at-all_) is like "fixing" infinite recursion by exiting after 42 recursion levels. At the end, all you get is an error message. In both cases, incompetent programmers focussed on the symptom instead of the problem. There is a similar story with doing lock-free programming, or using recursive mutexes, in order to avoid deadlocks. (In any cases, I am not "opposed" to weak references, or lock-free idioms. But they aren't magical devices.) – curiousguy May 31 '12 at 03:32
  • Sometimes the dependency is in the business rule and an explicit error message for the user is what's required. – Jean-Michaël Celerier Jun 02 '15 at 17:58
0

You'll get bad_weak_ptr exception if 'p' was (somehow) destroyed already. So it is safe is child ctor expects exceptions and not safe otherwise.

Alexander Poluektov
  • 7,844
  • 1
  • 28
  • 32
  • 2
    p can't validly be destroyed in this code, since the parent must still exist: we're in a member function called on it. I'd say that the child constructor should take a shared_ptr, use that to recover the child2 pointer, but then store a weak pointer to the parent. Or take two parameters. If you know that a caller has a shared_ptr, and the callee needs to use the value, then there's not much point passing it as a weak_ptr. If there's code elsewhere that creates child objects, and only has a weak_ptr, then maybe a constructor taking weak_ptr is right. – Steve Jessop Jan 26 '10 at 12:51
  • nice suggestion about the constructor taking a shared_ptr then converting to a weak_ptr for storage - I'll give that some thought (would certainly tidy up the code somewhat) – Alan Jan 26 '10 at 13:08
  • Btw, by "take two parameters", I meant one to the parent and one to the child2. Re-reading, I noticed that it could be read as one shared_ptr and one weak_ptr to the parent, but that's not what I meant. – Steve Jessop Jan 26 '10 at 16:37
  • 2
    @SteveJessop "_since the parent must still exist_", you didn't need a smart pointer in the first place. Using a regular pointer is the obvious, natural, easy, and efficient solution. – curiousguy Oct 09 '11 at 03:21