Advanced Polymorphism
Generalization and Specialization
- Achieved through interfaces and virtual functions, allowing different classes to be treated as instances of the same base class, particularly when implementing type-specific behavior.
class Animal {
public:
virtual void makeSound() {
cout << "Some generic sound" << endl;
}
};
class Dog : public Animal {
public:
void makeSound() override {
cout << "Bark" << endl;
}
};
class Cat : public Animal {
public:
void makeSound() override {
cout << "Meow" << endl;
}
};
// Usage
Animal* a1 = new Dog();
Animal* a2 = new Cat();
a1->makeSound(); // Outputs: Bark
a2->makeSound(); // Outputs: Meow
This example shows how polymorphism allows objects of different classes (
Dog
andCat
) to be treated as instances of the same base class (Animal
). ThemakeSound
function is overridden in each derived class to provide type-specific behavior.
Inclusion Polymorphism
- Inclusion Polymorphism: Also known as runtime polymorphism, it allows objects of derived classes to be treated as objects of the base class, enabling dynamic binding of functions at runtime.
- implementatopm: Typically achieved through inheritance and virtual functions, allowing for code flexibility and extensibility.
- Polymorphic Objects: Objects can exhibit different behaviors when accessed through base class pointers or references, depending on their actual derived type.
- Dynamic Binding: The function to be called is determined at runtime based on the actual type of the object, not the type of the pointer or reference used to access it.
class Animal {
public:
virtual void speak() {
cout << "Animal speaks" << endl;
}
};
class Dog : public Animal {
public:
void speak() override {
cout << "Dog barks" << endl;
}
};
// Usage
Animal* a = new Dog();
a->speak(); // Outputs: Dog barks (dynamic type is Dog)
Here, the dynamic type of the object
a
is determined at runtime. Althougha
is a pointer toAnimal
, it points to aDog
object, so theDog
'sspeak
function is called, demonstrating inclusion polymorphism.
Copying Polymorphic Objects
- Clone Function: To copy a polymorphic object, a
clone()
function is added to the abstract base class, allowing each derived class to define its specific cloning behavior. - Implementatio: The
clone()
function returns a pointer to a new object of the same derived type, enabling polymorphic copying.
class Shape {
public:
virtual Shape* clone() const = 0;
virtual void draw() = 0;
};
class Cube : public Shape {
public:
Cube* clone() const override {
return new Cube(*this); // Use copy constructor
}
void draw() override {
cout << "Drawing a Cube" << endl;
}
};
class Sphere : public Shape {
public:
Sphere* clone() const override {
return new Sphere(*this);
}
void draw() override {
cout << "Drawing a Sphere" << endl;
}
};
// Usage
Shape* original = new Cube();
Shape* copy = original->clone();
copy->draw(); // Outputs: Drawing a Cube
The clon
e function allows for the copying of polymorphic objects. In this example, the
Cubeclass overrides the
clone` function to return a new instance of itself using the copy constructor, demonstrating how each derived class can implement specific cloning behavior.
Specializing Operations with Dynamic Types
- Dynamic Casting: Used to safely downcast a base class pointer to a derived class type, enabling type-specific operations.
dynamic_cast
: Performs a runtime check to ensure the cast is valid, returning a null pointer if the cast fails.- Operator Overloading: Allows for type-specific behavior based on the dynamic type of the object.
class Shape {
public:
virtual bool operator==(const Shape& other) const = 0;
};
class Cube : public Shape {
private:
double side;
public:
Cube(double s) : side(s) {}
bool operator==(const Shape& other) const override {
const Cube* c = dynamic_cast<const Cube*>(&other);
return c && this->side == c->side;
}
};
// Usage
Cube c1(3.0), c2(3.0), c3(4.0);
cout << (c1 == c2); // Outputs: 1 (true)
cout << (c1 == c3); // Outputs: 0 (false)
The
operator==
is overloaded in theCube
class to compareCube
objects based on theirside
length. Thedynamic_cast
ensures that the comparison only occurs if both objects are of theCube
type, demonstrating how dynamic typing can be used to specialize operations.
Dynamic Type Identification
- Run-Time Type Information (RTTI):
typeid
anddynamic_cast
allow for run-time identification and manipulation of an object’s dynamic type. typeid
Operator: Used to determine the dynamic type of an object at runtime, returning atype_info
object that can be compared with other types.
class Shape {
public:
virtual void show() const {
if (typeid(*this) == typeid(Cube)) {
cout << "This is a Cube" << endl;
} else if (typeid(*this) == typeid(Sphere)) {
cout << "This is a Sphere" << endl;
}
}
};
class Cube : public Shape { /*...*/ };
class Sphere : public Shape { /*...*/ };
// Usage
Shape* s = new Cube();
s->show(); // Outputs: This is a Cube
The
show
function usestypeid
to check the dynamic type of the object at runtime. Depending on the type, it outputs whether the object is aCube
or aSphere
, demonstrating how RTTI can be used to identify and handle objects based on their actual type.