How to give a child a weak pointer to a parent? (C++)

Viewed 515

I've read here that, in a situation where some parent object uniquely owns several children, and each child needs to be able to access its parent, a weak pointer should be used as the back-pointer instead of a shared pointer so as to avoid a dependency cycle.

I have two questions:

  1. What are the advantages of giving a weak pointer to the parent as opposed to a simple reference?
  2. In the code below, how should I pass each child a weak pointer to its parent? I know you can inherit from std::enable_shared_from_this to get a shared pointer from this. Is there an equivalent for weak pointers? Or something else?
#include <memory>
#include <vector>

class Parent;

class Child {
 public:
  void set_parent(std::weak_ptr<Parent> parent) {
    parent_ = parent;
  }
  std::weak_ptr<Parent> parent_;
};

class Parent {
  void add_child(std::unique_ptr<Child> child) {
    // child->set_parent(???);
    children_.push_back(std::move(child));
  }
  std::vector<std::unique_ptr<Child>> children_;
};
2 Answers

weak_ptrs come from shared_ptrs. If your parent nodes "uniquely own" the children, then the children cannot have weak_ptrs to their parents, as this would require that a shared_ptr to them exist somewhere.

Note that the example you cite does not state anything about "unique ownership" of the relationship between the items.

Furthermore, so long as the parents never relinquish ownership of the children to code outside of the system, there's no reason to not just have a regular pointer to the parent (it needs to be a pointer because a reference cannot be changed). The pointer will always be valid (from outside of the system), since a parent must exist in order for the child to exist. That's what unique ownership means, after all.

You have said that a parent always uniquely owns a child but you have not specified a few things:

  1. Is it valid to copy a child object?
  2. Is it valid to move a child object?

If you answered yes to either of those then you must consider what happens if a child is copied or moved between parents as your child class wont by default prevent it. So my advice would be to either explicitly delete copy and move constructors and assignment operators OR if you want that ability then the responsibility is on YOU to make sure that if and when a child is copied and or moved that its parent pointer points to the correct parent and to prevent it pointing to an invalid / deleted parent.

If you go down the route of explicitly disabling copy and move semantics then in that circumstance i can't think of anything off the top of my head that would prevent you from using a reference to the parent instead.

Finally if you did actually want weak pointers in the children pointing to their parents then you could consider having your parent class extend from : enable_shared_from_this. I think the typical usecase for this is when you don't know if you will outlive another object but in the case where it is still a valid object then you would like to ensure it stays alive while you perform some operations on it.

Please note if you take that approach the parent object MUST be stored inside a shared_ptr. See shared_from_this. If not then you are in undefined behaviour territory and lets just all agree that is bad.

Edit: i forgot to mention if you can use C++17 you can directly get the weak_ptr from the parent to pass to the child: weak_from_this

Related