If I have a base abstract class with a function that takes it's own class as a parameter:
class Component abstract
{
public:
virtual bool Method(Component& other) = 0;
};
And I have a child class which overrides and overloads this function to take a parameter of its class:
class DerivedComponent : public Component
{
public:
virtual bool Method(Component& other) override;
virtual bool Method(DerivedComponent& other);
};
And I use these as components of another class, as such:
class Foo
{
private:
Component* m_component;
public:
Foo()
{
m_component = new DerivedComponent();
}
Component* GetComponent()
{
return m_component;
}
};
And I call the component's method, passing in Foo
's component:
Foo foo1 = Foo();
Foo foo2 = Foo();
foo1.GetComponent()->Method(*foo2.GetComponent());
Why does it call the DerivedComponent
's first un-overloaded method, Method(Component& other)
, instead of Method(DerivedComponent& other)
?
DerivedComponent
declares two overloads of Method
: one that that takes a Component &
and one that takes a DerivedComponent &
.
But overloading is always resolved statically. That is, the compiler has to decide at compile time which overloaded function is going to be called. Since that resolution happens a compile time, it's based on information that's available at compile time: the static type of pointer/reference the itself, not the dynamic type of the object to which it points/refers.
There is a fairly clean way to deal with it though--the visitor pattern (or at least a close variant of it).
To implement it we, add another virtual function (which I've called Dispatch
). When we use Method
, it calls Dispatch
on the object that was passed (by reference). So, we can get behavior that depends on both the object that did the calling and the the object that was passed. Here's a somewhat expanded version of your demo, showing virtual behavior for both the invoking object, and the passed object:
#include <iostream>
class Component
{
public:
virtual void Method(Component& other) {
std::cout << this->Dispatch() << "(" << other.Dispatch() << ")\n";
}
virtual std::string Dispatch() { return "Component"; };
};
class DerivedComponent : public Component
{
public:
std::string Dispatch() override { return "Derived"; }
};
struct Derived2 : public Component {
std::string Dispatch() override { return "Derived2"; }
};
// making this a template so it's easy to create `Foo`s that create and
// manage any of the preceding types.
template <class T>
class Foo
{
private:
Component* m_component;
public:
Foo()
{
m_component = new T;
}
Component* GetComponent()
{
return m_component;
}
};
int main() {
Foo<Component> foo;
Foo<DerivedComponent> foo1;
Foo<Derived2> foo2;
foo.GetComponent()->Method(*foo1.GetComponent());
foo1.GetComponent()->Method(*foo2.GetComponent());
foo2.GetComponent()->Method(*foo.GetComponent());
}
Result:
Component(Derived)
Derived(Derived2)
Derived2(Component)
Of course, in real use we need to handle a few other things--Foo
probably needs a dtor to destroy the Component
(or derived) object it creates. It probably also needs to do something with respect to copying, moving and assignment (but exactly what it should do is rather a separate question). Since we're using it as a base class, and probably going to destroy derived objects via a pointer/reference to the base, Component
needs a virtual dtor
. (And there may easily be a few more things that don't occur to me at the moment--but we're using inheritance, so we need to do all the usual inheritance "stuff").