Home | API | MFC | C++ | C | Previous | Next

Programming With C++

C++ Polymorphism

Polymorphism refers to the ability to be able to access different types of member functions depending on the type of object that invokes the function.  It happens when there is a hierarchy of classes and they are related by inheritance.  Polymorphism can be static or dynamic. In static polymorphism, memory will be allocated at compile time. In dynamic polymorphism, memory will be allocated at run-time.

C++ provides three different types of polymorphism.

Function Overloading

Is an example of static polymorphism. Function or method overloading is the ability to create multiple methods of the same name with different parameter implementations.  In the code section below the first function is called if the parameter is an int, the 2nd function is called if the parameter is a char and the third if the parameters are two ints.

#include <iostream>
using namespace std;
class base
{
public:
// function with int parameter
void func(int x)
{
cout << "value of x is " << x << endl;
}
// function with char parameter
void func(char x)
{
std::cout << "value of x is " <<x << " or "<<(int)x << endl;
}
// function with 2 int parameters
void func(int x, int y)
{
cout << "value of x and y is " << x << ", " << y << endl;
}
};
int main() { base obj1; // Which function is called will depend on the parameters passed
obj1.func(33); // The first 'func' is called
obj1.func('!'); // The second 'func' is called
obj1.func(33,66); // The third 'func' is called
return 0;
}

Operator Overloading

Operator overloading allows the programmer to define the bahaviour of an operator for a particular class. Almost all operators in C++ can be overloaded. To create an overloaded operator, an operator function is created to define the action of the overloaded operator. The function name is proceeded by the keyword "operator" and then the symbol for the operator is defined. An overloaded operator function can have a return type and a parameter list.

Generally speaking operators in C++ can be classified into two types: unary operators and binary operators.

Overloading the Binary Addition Operator - the following code segment illustrates how to overload the binary operator by adding two class objects

#include<iostream>
using namespace std;
class area {
private:
int x,y;
public:
area(int a = 0, int b =0) {x = a; y = b;} //initialise the object's value
// overloaded operator '+'
area operator + (area const &rhs) {
area returnobject; // Create an object to return
returnobject.x = x + rhs.x;
returnobject.y = y + rhs.y;
return returnobject;
}
void print()
{
cout << x << " " << y << endl;
}
};
int main()
{
area c1(10, 5), c2(2, 4); //declare to area object and initialise
area c3 = c1 + c2; // call to overloaded operator+
c3.print(); //output total area
}

Overloading the Unary increase and decrease Operator - the following code segment illustrates how to overload the unary increase and decrease Operator

#include <iostream>
using namespace std;
class Overloaded
{
private:
int count;
public:
Overloaded(): count(1){}
void operator ++() //Define overloaded prefix increment operator
{
count = count+1; //increase count by 1
}
void operator ++(int) //Define overloaded postfix increment operator
{
count = count+10; //increase count by 10
}
void operator --() //Define overloaded prefix decrement operator
{
count = count -1; //decrease count by 1
}
void operator --(int) //Define overloaded postfix decrement operator
{
count = count -10; //decrease count by 10
}
void DisplayCount () { cout<<"Count: " << count << endl; }
};
int main()
{
Overloaded value;
++value; //calls prefix increment operator
value.DisplayCount();
value++; //calls postfix increment operator
value.DisplayCount();
--value; //calls prefix decrement operator
value.DisplayCount();
value--;//calls postfix decrement operator
value.DisplayCount();
return 0;
}

Virtual Function - A virtual function is a member function which is declared within a base class and also defined in a derived class. When you refer to a derived class object using a pointer or a reference to the base class, the derived class function can be called by declaring a virtual function for that object. A virtual function is declared using the virtual keyword. When a function is made virtual, C++ determines which function is to be invoked at the run-time based on the type of the object pointed to by the base class pointer and hence is known as dynamic linkage or late binding.  

The code sample below demonstrates both early and late binding -

#include<iostream>
using namespace std;
class Base
{
public:
virtual void virtfunc()
{
cout << "Base class" << endl;
}
virtual void func()
{
cout << "Base non virtual class" << endl;
}
};
class Derived:public Base
{
public:
void virtfunc()
{
cout << "Derived Class" << endl;
}
void func()
{
cout << "Derived Class non virtual" <<endl;
}
};
int main()
{
Base* b; //Base class pointer
Derived d; //Derived class object
b = &d;
b->func(); // Early Binding calls base func
b->virtfunc(); // Late binding calls derived virtual func
}

Virtual Destructors

A virtual destructor ensures that when a derived subclass goes out of scope or is deleted the order of destruction of each class is carried out in the correct order. If the destruction order of the class objects is incorrect, it can lead to a memory leak because system resources are not deallocated. When a pointer to a base class is assigned to a derived class object and that object is deleted then the base class destructor will be called instead of the derived class destructor. To address this situation, the base class should be defined with a virtual destructor to ensure that the object of the derived class is destructed properly.

#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "Base Constructer" << endl;
}
virtual ~Base() // virtual destructor
{
cout << "Base Destructor" << endl;
}
};
class Derived : public Base {
public:
Derived() {
cout << "Derived Constructor" << endl;
}
~Derived() {
cout << "Derived Destructor" << endl;
}
};
void DeleteFishMemory(Base* pBase) {
delete pBase;
}
int main() {
cout << "Allocating a derived object on the free store:" << endl;
Derived* pBase = new Derived;
cout << "Deleting derived class " << endl;
DeleteFishMemory(pBase);
cout << "Instantiating a derived class on the stack:" << endl;
Derived drv;
cout << "Automatic destruction as it goes out of scope: " << endl;
return 0;
}

Pure Virtual Functions and Abstract Classes

An abstract class is a class which contains at least one pure virtual function. A pure virtual function is a virtual function that is required to be implemented by a derived class if the derived class is not abstract. Since abstract classes cannot be instantiated directly their purpose is to act as an Interface for its subclasses.  The ‘pure virtual function’ is declared in the base class and is assigned the 0.

In the example below a pure virtual function is created and implemented in the base class. Without this implementation, the compiler will throw an error -

#include <iostream>
using namespace std;
class Base
{
public:
virtual void pvf() = 0;// pure virtual function
Base(){ //constructor
cout << "pure virtual constructor called\n";
}
};
// This class inherits from Base and implements method pvf()
class Derived: public Base
{
public:
Derived(){ //constructor
cout << "derived constructor called \n";
}
void pvf() //implements put virtual function
{
}
};
int main(void)
{
Derived d;
return 0;
}

Characteristics of Abstract Class

Abstract class cannot be instantiated, but pointers and references of Abstract class type can be created.
Abstract class can have normal methods along with a pure virtual function.
Abstract classes are primarily used to provide an interface for any derived class.
Classes inheriting an Abstract Class must implement all pure virtual functions, or they will become Abstract too.


Home | API | MFC | C++ | C | Previous | Next
The Basics | Variables and Constants | Arrays | C-strings | Expressions and Operators | Controlling Program Flow | C++ Functions | Pointers and References | Memory Map and Free Store | Smart Pointers | Classes | Structures | Inheritance | Polymorphism | Templates | The Standard Template Library | The STL String Class | Namespace | Type Conversions | Input and Output Streams | The C++ Preprocessor | Exception Handling

Last Updated: 15 September 2022