120
votes

I am currently trying to learn how to use smart pointers. However while doing some experiments I discovered the following situation for which I could not find a satifying solution:

Imagine you have an object of class A being parent of an object of class B (the child), but both should know each other:

class A;
class B;

class A
{
public:
    void addChild(std::shared_ptr<B> child)
    {
        children->push_back(child);

        // How to do pass the pointer correctly?
        // child->setParent(this);  // wrong
        //                  ^^^^
    }

private:        
    std::list<std::shared_ptr<B>> children;
};

class B
{
public:
    setParent(std::shared_ptr<A> parent)
    {
        this->parent = parent;
    };

private:
    std::shared_ptr<A> parent;
};

The question is how can an object of class A pass a std::shared_ptr of itself (this) to its child?

There are solutions for Boost shared pointers (Getting a boost::shared_ptr for this), but how to handle this using the std:: smart pointers?

2
As is with any other tool you have to use it when it's appropriate. Using smart pointers for what you are doing is notYePhIcK
Similarly to boost. See here.juanchopanza
This is a problem at that level of abstraction. You don't even know that "this" points to memory on the heap.Vaughn Cato
Well, the language doesn't, but you do. As long as you keep track of what's where, you'll be fine.Alex

2 Answers

194
votes

There is std::enable_shared_from_this just for this purpose. You inherit from it and you can call .shared_from_this() from inside the class. Also, you are creating circular dependencies here that can lead to resource leaks. That can be resolved with the use of std::weak_ptr. So your code might look like this (assuming children rely on existence of parent and not the other way around):

class A;
class B;

class A
    : public std::enable_shared_from_this<A>
{
public:
    void addChild(std::shared_ptr<B> child)
    {
        children.push_back(child);

        // like this
        child->setParent(shared_from_this());  // ok
        //               ^^^^^^^^^^^^^^^^^^
    }

private:     
    // note weak_ptr   
    std::list<std::weak_ptr<B>> children;
    //             ^^^^^^^^
};

class B
{
public:
    void setParent(std::shared_ptr<A> parent)
    {
        this->parent = parent;
    }

private:
    std::shared_ptr<A> parent;
};

Note however, that calling .shared_from_this() requires that this is owned by std::shared_ptr at the point of call. This means that you cannot create such object on stack anymore, and generally cannot call .shared_from_this() from within a constructor or destructor.

10
votes

You have several problems in you design, that seem to stem from you misunderstanding of smart pointers.

Smart pointers are used to declare ownership. You are breaking this by declaring that both the parents owns all children, but also that each child own it's parent. Both can't be true.

Also, you are returning a weak pointer in getChild(). By doing so, you are declaring that the caller shouldn't care about the ownership. Now this can be very limiting, but also by doing so, you must make sure that the child in question won't get destroyed while any weak pointers are still held, if you would use a smart pointer, it would get sorted out by itself.

And the final thing. Usually, when you are accepting new entities, you should usually accept raw pointers. Smart pointer can have their own meaning for swapping children between parents, but for general usage, you should accept raw pointers.