Polymorphism in C++: Types, Techniques, & More
C++ is a widely adopted programming language in various domains, and its support for polymorphism is one of its key features.
Polymorphism in C++ is a feature of object-oriented programming languages that allows for the same code to be used in different contexts. It is a powerful tool that enables developers to write concise and reusable code. Polymorphism can be implemented in two forms: compile-time polymorphism, also known as static or early binding, and run-time polymorphism, also known as dynamic or late binding. In this blog, we will take a closer look at them and discuss their differences and implementation.
Polymorphism Meaning in C++
Polymorphism is an important concept in C++ programming which allows the same function to be used with different inputs. It enables a programmer to write code that can be adapted easily and quickly, as well as reduce large amounts of redundant code.
Polymorphism is also a key factor for object-oriented programming (OOP) since it allows multiple objects to respond differently when given the same input or command. Essentially, polymorphism means having multiple forms; in this case, it means having one function that can take on different forms depending on its parameters or arguments.
Examples of polymorphic functions are ones that take any type of data as parameters, such as overloading operators like + and * for arithmetic operations; virtual functions for dynamic binding between classes at runtime; templates for generic programming, etc.
For example, if we have two numbers: int x = 10 and float y = 20 then operator + will add both the value while operator * will multiply them together.
Types of Polymorphism in C++
Polymorphism can be categorized mainly into two parts. Let’s have a look at them in detail.
Compile-Time Polymorphism
It is a form of program that enables multiple classes to implement similar functions but with different behaviors. It has the following attributes:
- Static Binding- It relies heavily on static binding, meaning all the information necessary for making a call to a function must be known before compiling the code so that it can be solved during compilation.
- Operator Overloading- It is useful in situations where certain operations need to occur regardless of what data is passed through them, such as operator overloading which allows operators like addition and subtraction to act upon operands differently depending on their types.
- Function Overloading- It is useful in function overloading which lets programmers create multiple versions of the same function with varying parameters while still keeping code concise—this makes it easier for developers when writing programs since they don’t have duplicate codes.
- Template Programming- It provides another way to leverage compile-time polymorphism by allowing generic block codes that work in many contexts without needing modification each time. This further simplifies coding tasks by giving more flexibility when manipulating multiple values together at once instead of having separate instructions for every combination possible.
Run-Time Polymorphism
Run-time polymorphism, also known as dynamic binding, is a type of program that allows objects to respond differently according to their class type while still executing common parent behavior.
- Late Binding- This form relies heavily on techniques such as virtual functions and late binding since bindings cannot be determined until runtime due to a lack of information available beforehand.
- Object Instantiation- With this program, any changes made only take effect after object instantiation. This allows subclasses more control over how they behave compared with compile-time solutions which require all definitions upfront before running code.
- Speed- Characteristics-wise run-time polymorphism can be quite powerful yet at times it can slow down performance depending on what’s being done due to its reliance on processes happening affected. However, if used sparingly it should not cause drastic drops in speed or affect other components negatively.
Additionally, since no direct links between function call and definition exist, there may be issues arising due to miscommunication between two systems using different versions so best practice would always recommend checking both ends thoroughly before going live just to make sure. To learn more about polymorphism in C++ you can take up a professional C++ course and start building your career in the field.
Techniques to Implement Compile Type Polymorphism in C++
There are two methods through which compile-type polymorphism can be implemented. Let’s have a look.
Function Overloading
Function overloading is a form of Polymorphism available in C++ that lets programmers create multiple versions of the same function with varying parameters while still keeping code concise. This simplifies coding tasks by giving more control over how operations are performed without needing extensive modifications each time something needs changing either due to new data being received or other various reasons.
Since all the information necessary for making a call to the function (such as parameters and return type) must be known ahead of time, this method relies heavily on compile-time solutions so that bindings can be resolved during compilation itself rather than wait until runtime.
Here is an example for your reference,
{
public:
// function with 1 int parameter
void myFunc(int x)
{
cout << "x = " << x << "\n\n";
}
// myFunction with same name but 1 double parameter
void myFunc(double x)
{
cout << "x = " << x << "\n\n";
}
// myFunction with same name and 2 int parameters
void myFunc(int x, int y)
{
cout << "x = " << x << ", y = " << y << "\n\n";
}
};
int main()
{
functionOverloadingExample obj;
// Which myFunction is called will depend on the parameters passed
// The first 'myFunc' is called
obj.myFunc(15);
// The second 'myFunc' is called
obj.myFunc(15.30);
// The third 'myFunc' is not called because it takes two int parameters
// obj.myFunc(15, 30);
}
Output -
x = 15
x = 15.30
Operator Overloading
Operator overloading further expands what function overloading does by letting operators such as addition (+) and subtraction (-) act differently depending upon operands’ types. This way if two integers or strings are passed those will get manipulated accordingly but with different results, thus, adding more flexibility to coding tasks since many operations no longer require explicitly written instructions.
With operator overloading any changes made only take effect after object instantiation, thus, allowing subclasses more control over how they behave compared to static binding solutions which require all definitions upfront before running code properly and correctly. This should always be kept in mind prior to the use case implementation starts.
Here is an example for your reference,
class class_name {
public
return_type operator symbol (arguments) {
// define the function
}
};
#include <iostream>
using namespace std;
// define a class
class operatorOverlaodingExample
{
private:
int myVar;
public:
operatorOverlaodingExample(): myVar(10){}
void operator ++() {
myVar = myVar + 30;
}
void display() {
cout << "The updated value is: " << myVar;
cout << "\n\n";
}
};
int main()
{
operatorOverlaodingExample obj;
// call the function "void operator +++()"
++obj;
obj.display();
return 0;
}
Techniques to Implement Run Type Polymorphism in C++
There are two methods through which run-type polymorphism can be implemented. Let’s have a look.
Function Overriding
Function overriding further adds power to inheritance by allowing developers to create objects with a different behavior at runtime without changing any parameters or return types defined in the base/parent class’s declaration.
Here is a polymorphism example in C++,
#include <iostream>
using namespace std;
// define a base class
class animal
{
public:
// display function of the base class
void display()
{
cout << "It is the display function of the base class";
cout << "\n\n";
}
};
class dog:public animal
{
public:
// display function of the serived class
// this function will display()
// of base class override at run time
void display()
{
cout << "It is the display function of the derived class";
cout << "\n\n";
}
};
int main()
{
// create objects of base and child classes
animal y;
dog d;
// call the diplay() function
y.display();
d.display();
}
Virtual Functions
Run-time polymorphism can be achieved through virtual functions and dynamic binding between classes at runtime using pointers or references. This allows an object to call its own version of an overridden method instead of calling the parent’s version thereby achieving run-time polymorphism without changing any code at compile time.
Here is a polymorphism example in C++,
#include <iostream>
using namespace std;
// define a base class animal
class animal {
public:
// virtual function
virtual void display()
{
cout << "This is display in animal class." << "\n\n";
}
void print()
{
cout << "This is show in animal class." << "\n\n";
}
};
// define a child class dog
class dog : public animal {
public:
void display()
{
cout << "This is display in dog class." << "\n\n";
}
void print()
{
cout << "This is show in dog class." << "\n\n";
}
};
int main()
{
// create a reference of class animal
animal* aml;
dog d;
aml = &d;
// this will call the virtual function
aml->display();
// this will call the non-virtual function
aml->print();
}
Difference Between Compile-Time Polymorphism and Run-Time Polymorphism
Compile-time polymorphism and run-time polymorphism are two types of polymorphism in programming languages. Both involve allowing multiple classes to implement similar functions but with different behaviors. Let’s have a look at the differences.
Compile-Time Polymorphism | Run-Time Polymorphism |
---|---|
Occurs during compilation. | It occurs during program execution or run time. |
It is also called static or early binding polymorphism. | It is also called dynamic or late-binding polymorphism. |
It can be implemented through function overloading and operator overloading | It can be implemented through function overriding and virtual functions. |
Offers less flexibility to the programmers. | Offers more flexibility to the programmers. |
It is faster, as resolution occurs at compilation. | It is slower, as resolution occurs at runtime. |
Conclusion
In conclusion, polymorphism in C++ provides an effective way for developers to write code more efficiently and maintainable. It is a powerful tool that should be used judiciously since performance may suffer if applied too frequently or carelessly, but if implemented properly then developers can greatly benefit from its features while still ensuring quality standards remain consistent throughout their projects.