Composite pattern with parent pointer in C++

Started by
6 comments, last by BurrickSlayer 10 years, 6 months ago
Hello all,

I wanted to use the composite pattern in C++ but I encountered a problem in doing so.

My code looks like that:

class Component
{
    protected:
    
        Component* parent = nullptr;
};

class Leaf : public Component
{};

class Composite : public Component
{
    public:
    
        typedef std::unique_ptr<Component> ComponentPtr;
        
    public:
    
        void addChild(ComponentPtr child)
        {
            child->parent = this;
            // ...
        }
        
        auto removeChild(const Component& child) -> ComponentPtr;
};

The problem is that in the addChild method the protected parent member can't be accessed. I just have learned that an instance A can access the protected members of an instance B if and only if A and B are of the same type or B is derived from A. So the code shown above doesn't work for obvious reasons.

Now I ask myself how to solve the problem of not being able to access the parent member. Currently I can think of some possibilities:
  • Use a friend class/function/method to access the parent member.
  • Make the parent member public or provide a public getter/setter method.
  • Don't use the composite pattern. Instead have only a Component class that has both a parent and children.
I can't decide on the first option because I've hardly used friends in C++ so far.

I'm not too fond of the second option because setting the parent should not be part of the public API.

When using the third option every component would be a composite. Whether this is to be considered a bad thing probably depends on what the code is used for. In my case it wouldn't be so bad. If an object isn't supposed to have children I just won't add any.

What do you think? Are there any other alternatives?

Edit:

Unfortunately I have left out some relevant implementation details. As this caused some misunderstandings I would like to add the missing details here:

class Composite : public Component
{
    /* some code ommited */

    public:
    
        void addChild(ComponentPtr child)
        {
            child->parent = this;
            children.push_back(std::move(child));
        }

        /* some code ommited */
        
    private:

        std::vector<ComponentPtr> children;
};
Advertisement

A protected static function in Component would be able to access whatever you need in Component and be called from any Component.


static void SetParentOfChild(Component* child, Component* parent)
{
    child->parent = parent;
}

Note: I don't think you should be passing unique_ptr around by value. Would would addChild use a smart pointer and removeChild a const reference? You have to modify the child's parent member in both situations, right?

Do you need the parent pointer?
You can reverse the relationship and use composite-has-many-children, not component-has-a-parent.
@Pink Horror



A protected static function in Component would be able to access whatever you need in Component and be called from any Component.


Aah, thanks, I haven't thought of that. If I go with the composite pattern this will be a good option for setting the parent pointers.

Note: I don't think you should be passing unique_ptr around by value. Would would addChild use a smart pointer and removeChild a const reference? You have to modify the child's parent member in both situations, right?


I have left out some implementation details because I didn't consider them important at the time of writing my post. But now I see that the missing details have caused some misunderstandings. I'm sorry for that. I edited my first post accordingly.

The children are stored in a vector member of the composite: std::vector<ComponentPtr> children;
So this is what the addChild method actually looks like:

void addChild(ComponentPtr child)
{
    child->parent = this;
    children.push_back(std::move(child));
}

The unique_ptr is passed by value to show that the ownership of the component is transferred to the composite.

And yes, the removeChild method also modifies the child's parent member. This is done by an iterator returned by std::find_if, though, so the passed const reference stays untouched. I hope this clears things up.

But now as you say it I'm not sure anymore if declaring the reference const was a good idea. The child is modified after all, just by other means than using the passed reference.


@Hodgman

Do you need the parent pointer?
You can reverse the relationship and use composite-has-many-children, not component-has-a-parent.


Unfortunately I have left out implementation details in my first post because I considered them not important at the time of writing. That was a mistake. There are more details about what the Composite class actually looks like in my answer addressed to Pink Horror (and in the edit section of my first post).

So to be precise there is a bidirectional relationship between composites and their children: composite-has-many-children and component-has-a-parent.
The parent pointer is not absolutely needed indeed. It's just convenient to know a child's parent as it makes removing nodes easier. If the parents are unknown I need to iterate the whole structure in order to find the vector that containes the node to be removed.


-----

I'm not sure whether the composite pattern is the right choice in my particular case. Being able to treat components and composites the same is negligible for me because a visitor is used for iteration. Most components will be composites anyway so having a children vector in the leafs would be negligible too. Perhaps I'll just have a common base class for components that has both a parent and a children member. I need to think about that.

Thanks for your suggestions!

The children are stored in a vector member of the composite: std::vector children;
So this is what the addChild method actually looks like:

void addChild(ComponentPtr child)
{
child->parent = this;
children.push_back(std::move(child));
}
The unique_ptr is passed by value to show that the ownership of the component is transferred to the composite.

In order for child to be passed in, it has to be copied, which shouldn't compile. unique_ptr is not copy constructible or copy assignable. You're moving out of a temporary unique_ptr instead of the real one.

Children can be moved into the composites, so there is no copy construction required:

// Option 1
composite.addChild(Composite::ComponentPtr(new Component()));

// Option 2
Composite::ComponentPtr component(new Component());
// ... do some stuff with component ...
composite.addChild(std::move(component));

I like it because I think it's quite expressive this way and self-documents the transfer of ownership.

My first choice would be avoiding parent pointers, because both normal component operations (e.g. update or draw yourself and your children recursively) and "special" operations (e.g. delete a Component but place its children, if present, in a sensible Composite) shouldn't need them, and placing all Components in a single structural hierarchy seems wrong (what if you need different groupings for the same objects, e.g. pieces of articulated objects grouped by position for drawing and collision detection and by "limb" connectivity for physical simulation and animation?).

Can you clarify what you are unable to do without parent pointers? For example, why do you want to actually delete Components given only a pointer to them? You can instead set a "deleted" flag on the component, which should be good enough for the purpose of pretending the component doesn't exist, and later visit components recursively to delete their flagged children.

I'm not sure about a Composite abstract class a it appears in your code sample: it is almost trivial, and there can be many other Composite types beyond those with an array containing an arbitrary number of undifferentiated children.

Another bad thing is the use of unique_ptr and of containers that own child Components. It's another enemy of multiple Composite hierarchies and of flexible destruction of Components; a std::vector or individual variables of type Component* would let you move child components to other composites or delete them as you need when you delete or recycle a composite.

Omae Wa Mou Shindeiru

Can you clarify what you are unable to do without parent pointers? For example, why do you want to actually delete Components given only a pointer to them? You can instead set a "deleted" flag on the component, which should be good enough for the purpose of pretending the component doesn't exist, and later visit components recursively to delete their flagged children.

The parent pointers aren't necessarily needed and now I'll try to do without them. I had them mainly for convenience so I could remove a component from its composite more easily.
In any case the deleted flag is a good idea and I'll have this implemented. Such a flag should also work nice along with std::remove_if. Thanks for your suggestion.


Another bad thing is the use of unique_ptr and of containers that own child Components. It's another enemy of multiple Composite hierarchies and of flexible destruction of Components; a std::vector or individual variables of type Component* would let you move child components to other composites or delete them as you need when you delete or recycle a composite.

I agree with you that a unique_ptr wouldn't be a good choice for components that are shared by multiple composites. My sample code was written with unshared components in mind (a simple tree structure), so that's why I decided for unique_ptr.

This topic is closed to new replies.

Advertisement