The problem doesn’t lie in tracking down the error in case of a code change, but rather having a well organized, structured and abstracted code base. If you have to pass a pointer to the parent on creation of a child object, everyone who reads this, or the header file of the child class, will notice that the child requires access to some stuff of the parent.
If you just silently cast the parent component, it can lead to some real complicated bugs, where something is modified by a child object, but you don’t realize that because it isn’t apparent that it needs to modify/access the parent.
Also if you use “callback” functions, you aren’t limited to a specific parent class:
In my opinion not so good:
class Child, public Component
{
public:
Child (Parent* parent) { this->parent = parent; }
~Child() = default;
void resized() override { parent->childUpdated(); }
private:
Parent* parent;
}
and
class Parent
{
public:
Parent() { child = make_unique<Child>(this); }
~Parent() = default;
void childUpdated() { /* Do something */ }
private:
unique_ptr<Child> child;
}
vs in my opinion better
class Child, public Component
{
public:
Child() = default;
~Child() = default;
void resized() override { onUpdate(); }
std::function<void()> onUpdate;
}
and
class Parent
{
public:
Parent()
{
child = make_unique<Child>();
child.onUpdate = [this] () { childUpdated(); };
}
~Parent() = default;
void childUpdated() { /* Do something */ }
private:
unique_ptr<Child> child;
}
because in this case you can also do
class CompletelyOtherParent
{
public:
CompletelyOtherParent()
{
child = make_unique<Child>();
child.onUpdate = [this] () { childUpdatedDifferent(); };
}
~CompletelyOtherParent() = default;
void childUpdatedDifferent() { /* Do something */ }
private:
unique_ptr<Child> child;
}
without the need to modify the Child
class