In C++, dynamic casting is a type of casting that allows you to perform runtime type checking and safely convert pointers or references of a base class to pointers or references of a derived class. It is primarily used in polymorphic class hierarchies, where objects of different derived classes can be accessed through a common base class pointer or reference.
Here’s an example to illustrate dynamic casting in C++:
#include <iostream>
class Base {
public:
virtual void print() {
std::cout << "This is the base class." << std::endl;
}
};
class Derived : public Base {
public:
void print() override {
std::cout << "This is the derived class." << std::endl;
}
void additionalFunction() {
std::cout << "This function is only available in the derived class." << std::endl;
}
};
int main() {
Base* basePtr = new Derived();
// Attempting dynamic_cast to Derived* from Base*
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
if (derivedPtr) {
derivedPtr->print(); // Accessing derived class function
derivedPtr->additionalFunction(); // Accessing additional function of derived class
} else {
std::cout << "Failed to perform dynamic cast." << std::endl;
}
delete basePtr;
return 0;
}
In this example, we have a base class Base
and a derived class Derived
that inherits from the base class. In the main()
function, we create a pointer basePtr
of type Base*
pointing to an object of the derived class.
Next, we use dynamic casting to convert basePtr
to a Derived*
by using the syntax dynamic_cast<Derived*>(basePtr)
. If the dynamic cast succeeds (i.e., the object being pointed to is actually of the derived class), the resulting pointer derivedPtr
will be valid, and we can use it to call functions specific to the derived class, such as print()
and additionalFunction()
.
If the dynamic cast fails (i.e., the object being pointed to is not of the derived class), the resulting pointer derivedPtr
will be a null pointer. In such cases, we can handle the failure accordingly.
Note that dynamic casting is only applicable for pointers or references to classes that have at least one virtual function. It provides runtime type checking and allows for safer type conversions between polymorphic classes.
Upcasting and downcasting are two types of casting operations that are commonly used when working with inheritance and polymorphism.
- Upcasting: Upcasting involves converting a pointer or reference of a derived class to a pointer or reference of its base class. It is safe and can be done implicitly. Here’s an example:
#include <iostream>
class Animal {
public:
virtual void sound() {
std::cout << "Animal makes a sound." << std::endl;
}
};
class Dog : public Animal {
public:
void sound() {
std::cout << "Dog barks." << std::endl;
}
};
int main() {
Dog dog;
Animal* animalPtr = &dog; // Upcasting
// Calling the base class function using the derived class pointer
animalPtr->sound(); // Output: "Dog barks."
return 0;
}
In this example, we have a base class Animal
and a derived class Dog
inheriting from Animal
. The sound()
function is overridden in the Dog
class. Inside the main()
function, we create an object of Dog
and assign its address to a pointer of type Animal*
. This is an example of upcasting. We can call the sound()
function through the animalPtr
, and it will invoke the derived class’s implementation.
- Downcasting: Downcasting involves converting a pointer or reference of a base class to a pointer or reference of its derived class. It is not safe and requires an explicit cast. Here’s an example:
#include <iostream>
class Animal {
public:
virtual void sound() {
std::cout << "Animal makes a sound." << std::endl;
}
};
class Dog : public Animal {
public:
void sound() {
std::cout << "Dog barks." << std::endl;
}
void fetch() {
std::cout << "Dog fetches a ball." << std::endl;
}
};
int main() {
Animal animal;
Animal* animalPtr = &animal;
animalPtr->sound(); // Output: "Animal makes a sound."
// Downcasting
Dog* dogPtr = dynamic_cast<Dog*>(animalPtr);
if (dogPtr != nullptr) {
dogPtr->sound(); // Output: "Dog barks."
dogPtr->fetch(); // Output: "Dog fetches a ball."
}
else {
std::cout << "Failed to downcast to Dog." << std::endl;
}
return 0;
}
In this example, we have the same Animal
and Dog
classes as before. Inside the main()
function, we create an object of Animal
and assign its address to a pointer of type Animal*
. Then we attempt to downcast the animalPtr
to a Dog*
using the dynamic_cast
operator. Since animalPtr
is not pointing to a Dog
object, the downcast will fail, and dogPtr
will be nullptr
. We check for the validity of the downcast and print appropriate output.
It’s important to note that downcasting is only safe if the object being downcasted is actually an instance of the derived class. Otherwise, it may lead to undefined behavior or runtime errors. To avoid such issues, it is recommended to use dynamic_cast
and check for nullptr
before using the downcasted pointer.