I saw the following question and I asked myself if there is a better way to address this problem, so there is no need for a cast. Consider the following code:
#include <iostream>
class Base
{
public:
virtual ~Base() {}
};
class Derived : public Base
{
protected:
int someVar = 2;
public:
int getSomeVar () {return this->someVar;}
};
int main()
{
Base B = Base();
Derived D = Derived();
Base *PointerToDerived = &D;
Base *PointerToBase = &B;
std::cout << dynamic_cast<Derived*>(PointerToDerived)->getSomeVar() << "\n"; //this will work
std::cout << dynamic_cast<Derived*>(PointerToBase)->getSomeVar() << "\n"; //this will create a runtime error
return 0;
}
Is there a better way to design this, so no cast is needed and runtime errors like this can be avoided?
The visitor pattern use double dispatch to allow you to use class-specific member function and member variable (opposed to member function/variable available through the hierarchy).
To implement the visitor pattern, you need a visitor and a hierarchy of visited (in your example it's Base
and the classes derived from Base
).
With your example it would give something like that:
class Base
{
public:
virtual ~Base() {}
virtual void visit(Visitor) = 0;
};
class Derived : public Base
{
protected:
int someVar = 2;
public:
int getSomeVar () {return this->someVar;}
void visit(Visitor& v) {
v.visit(this);
}
};
class Visitor {
public:
void visit(Derived& d) {
bar(d.someVar);
}
};
The idea behind the visitor is that this
of Derived
know it's real type while a polymorphic variable (a Base&
or Base*
) don't. OverridingBase::visit
allow you to call the visit
who will do the dispatch to the right Visitor::visit
.
If you don't override Base::visit
, when calling it on a Base&
(or Base*
) it will call the function on a Base
object (so this
will be of type Base*
). That's why in the example Base::visit
is abstract, doing otherwise will only make error more likely to happen (like forgetting to override visit
).
When adding a new type in the Base
hierarchy (for example a Derived2
class), you will need to add two functions: Derived2::visit
and Visitor::visit(Derived2)
(or Visitor::visit(Derived2&)
).
EDIT