If you pass b
into bar()
without changing anything else, bar()
can only access the A
within its parameter, a problem known as Object Slicing.
The solution is to pass b
into bar()
either by reference or via a pointer, even though the parameter is of type A
you can use a dynamic_cast<>
to try and get back to the derived object.
Pass it in as follows:
bar(&b);
and rewrite bar
like this:
void bar(A *foo_)
{
B *foo_as_B = dynamic_cast<B *>(foo_);
if (foo_as_B != nullptr)
{
cout << foo_as_B->foo << endl;
}
}
Then it'll work.
dynamic_cast<>
is capable of casting a pointer/reference to a superclass into a pointer/reference to a subclass, provided the superclass object actually was originally instantiated as the subclass. It's actually a bit more complex than this, but just for getting started that will be sufficient.
In the pointer case, if the cast is OK, you get a valid pointer. If the cast can't be done, the pointer version returns nullptr. Hence the reason for checking it before using it.
Good defensive coding says you should always check pointers from unknown sources. Even though you may believe that the cast will succeed and you may be right at the time you write bar(), in production code, it's entirely possible that some other programmer will come along two or three years later and add the following:
class C : public A
{
....
}
C c{};
bar(&c);
At which point your check for nullptr will save you from the demon of undefined behavior.
You can also do it with references, but the failure case here is different: it throws an exception, which you'd probably want to catch.
This question shows the details of this.
-- Edit --
In response to the notes about getting error C2683: 'dynamic_cast': 'A' is not a polymorphic type
, this SO Question explains why. The standard states that there must be at least one virtual function for dynamic_cast<>
to work, that's what being polymorphic means.
I have never bumped into this because it's a reflex action now for me to make all destructors virtual in production code. The issue of why you want to make destructors virtual is a whole separate topic which I am not going to dig into here. Suffice it to say that it does have its advantages to do so.
All that said, the fix is to change class A
as follows:
class A
{
public:
virtual ~A() {}
};