1
\$\begingroup\$

I have a class named Object, which is a common interface for all its derived class.

The following rules apply to all Object instances.

  1. As zero or one parent.
  2. As zero or more child.
  3. Can not be the parent of one of its ancestor, nor itself.
  4. Deletes its children on destruction.
  5. Children can leave and join new parent.
  6. Parents can not abandon a child, nor adopt a new one without its consent.

Object.hpp

#ifndef OBJECT_HPP
#define OBJECT_HPP

#include <unordered_set>

namespace Foo 
{
class Object
{
public:

  explicit Object() : m_parent(nullptr), m_children() {}

  explicit Object(Object* parent);

  // Copy constructor is not allowed
  Object(const Object& orig) = delete;

  // Assignation operator is not allowed
  Object& operator =(const Object& orig) = delete;

  virtual ~Object();

  int countChildren() const;

  const Object* getParent() const;

protected:

  void joinParent(Object* parent);

  void quitParent();

private:

  // data members

  std::unordered_set<Object*> m_children;

  Object* m_parent;

  // methods

  void addChild(Object* child);

  void removeChild(Object* child);

};
}

#endif // OBJECT_HPP

Object.cpp

#include "Object.hpp"

using namespace Foo;


// constructor/destructor

Object::Object(Object* parent) : m_parent(nullptr), m_children()
{
  joinParent(parent);
}

Object::~Object()
{
  quitParent(); // Notify its parent

  // Delete all its children. Rule 4.
  while (!m_children.empty())
  {
    delete *(m_children.begin());
  }
}


// public methods

int Object::countChildren() const
{
  return m_children.size();
}

const Object* Object* getParent() const
{
  return m_parent;
}


// protected methods

void Object::joinParent(Object* parent)
{
  Object* ancestor = parent;

  // Check if respect tree hierarchy. Rule 3.
  while (ancestor != nullptr)
  {
    if (ancestor == this)
      // Foo::Error derived from std::runtime_error
      throw Foo::Error("No respect of tree hierarchy");

    ancestor = ancestor->m_parent;
  }

  quitParent(); // Rule 1.
  m_parent = parent;
  m_parent->addChild(this);
}

void Object::quitParent()
{
  if (m_parent != nullptr)
  {
    m_parent->removeChild(this); // Notify its parent.
    m_parent = nullptr;
  }
}


// private methods


/* 
 *Methods addChild and removeChild are private
 *to ensure that rules 5 and 6 are respected.
 */

void Object::addChild(Object* child)
{
  m_children.insert(child);
}

void Object::removeChild(Object* child)
{
  m_children.erase(child);
}

After testing my interface, I've come with the following conclusions.

  • Auto deletation of children can result in dangling pointers.
  • Stack instances have undefined behavior, resulted of deleting children. (one time in parent destructor, second time when out of scope)

Now the questions

  1. Is there a way to change my interface so that its more safe?
  2. Should I remove rule 4?
  3. Is there any leak in my tree hierarchy logic? (e.g possible circular hierarchy)

Remark : I use c++11 standard. However, I'm open to see any answer with higher standard.

\$\endgroup\$
  • \$\begingroup\$ With the design you're throwing away C++'s type system. I don't see any real reason to use C++ in that case. \$\endgroup\$ – Incomputable Sep 1 '17 at 15:05
  • \$\begingroup\$ It follows the same interface as Qt framework. \$\endgroup\$ – O. Dion Sep 1 '17 at 15:27
  • \$\begingroup\$ Qt has specific problem in mind, specifically GUI. Your problem description doesn't seem to mention. As a result we ask questions. Please be more specific in what problem you're solving. \$\endgroup\$ – Incomputable Sep 1 '17 at 15:30
  • \$\begingroup\$ By constructing a tree hierarchy, I have in mind to call a single method on a single instance, that will propagate to its children and so on. For example, root->paintEvent() will call paintEvent on every its descandant. I also implement the deletation rule, but I'm not sure if it's a good thing \$\endgroup\$ – O. Dion Sep 1 '17 at 15:44
  • \$\begingroup\$ I believe you meant deletion? Yeah, it kind of goes against standard C++, but is probably fine for your usage. I believe it is much better to inherit from QObject and add the stuff you need. You can also hide stuff you don't want. Reimplementing that looks like time wasting and error prone. \$\endgroup\$ – Incomputable Sep 1 '17 at 15:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.