Polymorphism
Virtual Functions
Abstract Classes
Polymorphism stands for multiple forms
It is one of the main aspects of the Object-Oriented Paradigm
It allows the "same" operation to be performed differently depending on the type of object
Imagine that we have an array of pointers to objects of different classes
Polymorphism means that, the same function call, applied to different elements of this array, will result in a different method being called
Polymorphism is hampered in C++, because it resolves function calls during compile time
I.e., each function call in your source code is mapped to the function definition before the application starts
This process is called early-binding
#include <iostream>
const int N = 3;
class Animal
{
public:
void speak() const
{
std::cout << "Animal sound!" << std::endl;
}
};
class Dog : public Animal
{
public:
void speak() const
{
std::cout << "Woof!" << std::endl;
}
};
class Cat : public Animal
{
public:
void speak() const
{
std::cout << "Meow!" << std::endl;
}
};
void speak(const Animal &animal)
{
animal.speak();
}
int main()
{
Animal *animals[N];
char choice = '\0';
for (int i = 0; i < N; i++)
{
std::cout << "Want to add a dog [d] or cat [c]: ";
std::cin >> choice;
if (choice == 'd')
{
animals[i] = new Dog();
}
else
{
animals[i] = new Cat();
}
}
for (int i = 0; i < N; i++)
{
speak(*animals[i]);
}
for (int i = 0; i < N; i++)
{
delete animals[i];
}
return 0;
}
The solution introduced in C++ was to introduce the keyword virtual
It can be applied to functions in a base class
It instructs C++ to implement dynamic dispatch, instead of early-binding
Dynamic dispatch means that the mapping from function call to function definition occurs during run time, based on the type of the object
#include <iostream>
const int N = 3;
class Animal
{
public:
virtual void speak() const
{
std::cout << "Animal sound!" << std::endl;
}
};
class Dog : public Animal
{
public:
void speak() const override
{
std::cout << "Woof!" << std::endl;
}
};
class Cat : public Animal
{
public:
void speak() const override
{
std::cout << "Meow!" << std::endl;
}
};
void speak(const Animal &animal)
{
animal.speak();
}
int main()
{
Animal *animals[N];
char choice = '\0';
for (int i = 0; i < N; i++)
{
std::cout << "Want to add a dog [d] or cat [c]: ";
std::cin >> choice;
if (choice == 'd')
{
animals[i] = new Dog();
}
else
{
animals[i] = new Cat();
}
}
for (int i = 0; i < N; i++)
{
speak(*animals[i]);
}
for (int i = 0; i < N; i++)
{
delete animals[i];
}
return 0;
}
The keyword override
was introduced in the C++11 Standard:
→ It enhances code safety by providing compile-time checking
→ It Improves code readability by indicating the intention of overriding a base class function
To ensure that the destructor of the derived class is called, you should declare the base class destructor as virtual
Otherwise, the destructor of the base class might be called, leading to memory leaks
#include <iostream>
const int N = 3;
class Animal
{
public:
virtual void speak() const
{
std::cout << "Animal sound!" << std::endl;
}
virtual ~Animal()
{
std::cout << "~Animal() called." << std::endl;
}
};
class Dog : public Animal
{
public:
void speak() const override
{
std::cout << "Woof!" << std::endl;
}
~Dog() override
{
std::cout << "~Dog() called." << std::endl;
}
};
class Cat : public Animal
{
public:
void speak() const override
{
std::cout << "Meow!" << std::endl;
}
~Cat() override
{
std::cout << "~Cat() called." << std::endl;
}
};
void speak(const Animal &animal)
{
animal.speak();
}
int main()
{
Animal *animals[N];
char choice = '\0';
for (int i = 0; i < N; i++)
{
std::cout << "Want to add a dog [d] or cat [c]: ";
std::cin >> choice;
if (choice == 'd')
{
animals[i] = new Dog();
}
else
{
animals[i] = new Cat();
}
}
for (int i = 0; i < N; i++)
{
speak(*animals[i]);
}
for (int i = 0; i < N; i++)
{
delete animals[i];
}
return 0;
}
An abstract class is a base class that defines an interface
A concrete class is a derived class that implements that interface
The abstract class exposes the interface to its clients
An abstract class has at least one pure virtual function
The syntax to create a pure virtual function is:
virtual returnType functionName(arguments) = 0;
A pure virtual function does not need a definition - you can omit its implementation detail
Objects of an abstract class cannot be instantiated
#include <iostream>
const int N = 3;
class Animal
{
public:
virtual void speak() const = 0;
virtual ~Animal()
{
std::cout << "~Animal() called." << std::endl;
};
};
class Dog : public Animal
{
public:
void speak() const override
{
std::cout << "Woof!" << std::endl;
}
~Dog()
{
std::cout << "~Dog() called." << std::endl;
}
};
class Cat : public Animal
{
public:
void speak() const override
{
std::cout << "Meow!" << std::endl;
}
~Cat()
{
std::cout << "~Cat() called." << std::endl;
}
};
void speak(const Animal &animal)
{
animal.speak();
}
int main()
{
Animal *animals[N];
char choice = '\0';
for (int i = 0; i < N; i++)
{
std::cout << "Want to add a dog [d] or cat [c]: ";
std::cin >> choice;
if (choice == 'd')
{
animals[i] = new Dog();
}
else
{
animals[i] = new Cat();
}
}
for (int i = 0; i < N; i++)
{
speak(*animals[i]);
}
for (int i = 0; i < N; i++)
{
delete animals[i];
}
return 0;
}