Hi I am trying to learn what dynamic cast is, and everything i read just leaves me more confused than I was. Hoping someone could explain it in easy terms. What I think it might be is just turning one pointer into a different type, ie a pointer created from a base class into a derived pointer (or vice versa). But the wording from everywhere else I have read, makes it seem like it may be a lot more than that.
You can always take a pointer to one type, and pretend it's a pointer to a different type. Always. It might be a terrible idea and you're about to cause a crash, but you can do it.
dynamic_cast comes with a check that the conversion actually makes sense.
If it isn't, the dynamic_cast fails and you see that and then you don't try to use an object as something it isn't and then your program doesn't crash.
// runtime type identification (RTTI) using dynamic_cast<>
#include <iostream>
class Fish
{
public:
virtualvoid Swim()
{
std::cout << "Fish swims in water\n";
}
// base class should always have virtual destructor
virtual ~Fish()
{}
};
class Tuna : public Fish
{
public:
void Swim()
{
std::cout << "Tuna swims real fast in the sea\n";
}
void BecomeDinner()
{
std::cout << "Tuna became dinner in Sushi\n";
}
};
class Carp : public Fish
{
public:
void Swim()
{
std::cout << "Carp swims real slow in the lake\n";
}
void Talk()
{
std::cout << "Carp talked crap\n";
}
};
void DetectFishType(Fish* InputFish)
{
Tuna* pIsTuna = dynamic_cast<Tuna*>(InputFish);
if (pIsTuna)
{
std::cout << "Detected Tuna. Making Tuna dinner:\n";
pIsTuna->BecomeDinner(); // calling Tuna::BecomeDinner
}
Carp* pIsCarp = dynamic_cast<Carp*>(InputFish);
if (pIsCarp)
{
std::cout << "Detected Carp. Making carp talk:\n";
pIsCarp->Talk(); // calling Carp::Talk
}
std::cout << "Verifying type using virtual Fish::Swim:\n";
InputFish->Swim(); // calling virtual function Swim
}
int main()
{
Carp myLunch;
Tuna myDinner;
DetectFishType(&myDinner);
std::cout << '\n';
DetectFishType(&myLunch);
}
I don't know what compiler you use, you may have to enable RTTI for this to work. How you do that I don't have a clue. With VS 2019 it is enabled by default apparently.
Some forms of dynamic_cast rely on runtime type identification (RTTI), that is, information about each polymorphic class in the compiled program. Compilers typically have options to disable the inclusion of this information.
Other IDEs might act differently with RTTI. I don't know for sure.
The most common use for dynamic casting is for converting base-class pointers into derived-class pointers. This process is called downcasting. If the conversion is not correct then either a nullptr is returned or an exception is raised.
In the above code, myLunch is of type Carp. This is upcasted to a pointer to type Fish (base class). This can only properly be downcasted to a pointer to Carp - as this was it's original class type. If it is tried to be downcast to *Carp, then this is OK but if tried to downcast to *Tuna then this is an error as the original type wasn't Tuna. Conversely for myDinner, this can be successfully downcasted to a *Tuna, but not *Carp.
A re-interpret cast could be used, but this would try to do the cast in all circumstances - irrespective of whether it made sense or not. Casting *Carp as a *Tuna makes no sense. reinterpret cast would allow it, but dynamic cast won't.
I would add that as a rule of thumb, which other people may of course disagree with; if I see a dynamic_cast in the code, I take that to mean "oh dear, this has gone wrong and I really should redesign my solution, but instead I'll just forcibly smooth over my design mistakes".
It's a warning sign to me; it's not always the case, of course, but it often suggests that the design has gone wrong.
Yeah, I know, not always, but we are all the sum of our experience and I've seen it used to paper over design errors far more than for good reasons.
If you've used it only once, then I expect it was necessary. When you can grep the codebase of a single software product (as I have just this second done) and see it 290 times spread across some 50 libraries, it feels a lot more like 290 mistakes.
I've had to use reinterpret_cast for a Win32 API app when changing over from using C-style casts. Most changes from C-style casting static_cast worked.