Polymorphism
- The word polymorphism means many shapes or having many forms. This is achieved by operator overloading, coercion, function overloading and function overriding, allowing operators and functions to behave differently in different context.
Operator Overloading: We use operator overloading for the operators to operate on objects.
Function overloading : Same function name can be used for different function with different
arguments.
Function Overriding : Also known as Inclusion means polymorphism due to inheritance. Here
functions behave
differently due to inheritance.
Coercion: Also known as implicit type conversion is an automatic type conversion
by the compiler.
In a mixed-type expression, data of one or more subtypes can be converted
to a supertype as
needed at runtime so that the program will run correctly. For example,
double d;
long l;
int i;
if (d > i)
d = i;
if (i > l)
l = i;
if (d = = l) d *= 2;
Although d, l and i belong to different data types, they will be automatically converted to equal data
types each time a comparison or assignment is executed.
This behavior should be used with caution, as unintended consequences can arise. Data can be lost
when floating-point representations are converted to integral representations as the fractional
components of the floating-point values will be truncated (rounded down). For example, int a = 3.5;
Conversely, converting from an integral representation to a floating-point one can also lose precision,
since the floating-point type may be unable to represent the integer exactly. For example, float f =
16777217;
float might be an IEEE 754 single precision type, which cannot represent the integer 16777217
exactly, while a 32-bit integer type can. This can lead to situations such as storing the same integer
value into two variables of type integer and type real which return false if compared for equality.
Explicit type casting is the solution to these problems.
Function overloading : Same function name can be used for different function with different
arguments.
Function Overriding : Also known as Inclusion means polymorphism due to inheritance. Here
functions behave
differently due to inheritance.
Coercion: Also known as implicit type conversion is an automatic type conversion
by the compiler.
In a mixed-type expression, data of one or more subtypes can be converted
to a supertype as
needed at runtime so that the program will run correctly. For example,
double d;
long l;
int i;
if (d > i)
d = i;
if (i > l)
l = i;
if (d = = l) d *= 2;
Although d, l and i belong to different data types, they will be automatically converted to equal data
types each time a comparison or assignment is executed.
This behavior should be used with caution, as unintended consequences can arise. Data can be lost
when floating-point representations are converted to integral representations as the fractional
components of the floating-point values will be truncated (rounded down). For example, int a = 3.5;
Conversely, converting from an integral representation to a floating-point one can also lose precision,
since the floating-point type may be unable to represent the integer exactly. For example, float f =
16777217;
float might be an IEEE 754 single precision type, which cannot represent the integer 16777217
exactly, while a 32-bit integer type can. This can lead to situations such as storing the same integer
value into two variables of type integer and type real which return false if compared for equality.
Explicit type casting is the solution to these problems.
- There are two types of polymorphism: Adhoc Polymorphism and Universal Polymorphism.
Adhoc Polymorphism:
means temporary polymorphism. This is achieved by operator overloading and coercion. Here operators behave differently for some particular statement of a
program.
means temporary polymorphism. This is achieved by operator overloading and coercion. Here operators behave differently for some particular statement of a
program.
Universal Polymorphism:
means permanent polymorphism. This is achieved by function overloading and function overriding (inclusion). Here functions behave polymorphic throughout
the program.
means permanent polymorphism. This is achieved by function overloading and function overriding (inclusion). Here functions behave polymorphic throughout
the program.
- Polymorphism is supported by C++ both at Compile Time and at Run Time.
Compile-time polymorphism is achieved by operator overloading, coercion and function overloading.
Run-time polymorphism is accomplished by using function overriding and virtual functions.
Virtual Functions
- A virtual function is a member function that is declared within a base class and redefined by a
derived class. To create a virtual function, precede the function’s declaration in the base class with
the keyword virtual. When a class containing a virtual function is inherited, the derived class
redefines the virtual function to fit its own needs. In essence, virtual functions implement the “one
interface, multiple methods” philosophy that underlies polymorphism. The virtual function within
the base class defines the form of the interface to that function. Each redefinition of the virtual
function by a derived class implements its operation as it relates specifically to the derived class.
That is, the redefinition creates a specific method.
the keyword virtual. When a class containing a virtual function is inherited, the derived class
redefines the virtual function to fit its own needs. In essence, virtual functions implement the “one
interface, multiple methods” philosophy that underlies polymorphism. The virtual function within
the base class defines the form of the interface to that function. Each redefinition of the virtual
function by a derived class implements its operation as it relates specifically to the derived class.
That is, the redefinition creates a specific method.
- To begin, examine this short example:
#include <iostream>
using namespace std;
class Base {
public:
virtual void vfunc() { cout << “This is Base’s vfunc().\n”; }
};
class Derived1 : public Base {
public:
void vfunc() { cout << “This is Derived1′s vfunc().\n”; }
};
class Derived2 : public Base {
public:
void vfunc() { cout << “This is Derived2′s vfunc().\n”; }
};
int main()
{
Base *p, b;
Derived1 d1;
Derived2 d2;
p = &b; // point to Base
p->vfunc(); // access base’s vfunc()
p = &d1; // point to Derived1
p->vfunc(); // access Derived1′s vfunc()
p = &d2; // point to Derived2
p->vfunc(); // access Derived2′s vfunc()
return 0;
}
This program displays the following:
This is Base’s vfunc().
This is Derived1′s vfunc().
This is Derived2′s vfunc().
using namespace std;
class Base {
public:
virtual void vfunc() { cout << “This is Base’s vfunc().\n”; }
};
class Derived1 : public Base {
public:
void vfunc() { cout << “This is Derived1′s vfunc().\n”; }
};
class Derived2 : public Base {
public:
void vfunc() { cout << “This is Derived2′s vfunc().\n”; }
};
int main()
{
Base *p, b;
Derived1 d1;
Derived2 d2;
p = &b; // point to Base
p->vfunc(); // access base’s vfunc()
p = &d1; // point to Derived1
p->vfunc(); // access Derived1′s vfunc()
p = &d2; // point to Derived2
p->vfunc(); // access Derived2′s vfunc()
return 0;
}
This program displays the following:
This is Base’s vfunc().
This is Derived1′s vfunc().
This is Derived2′s vfunc().
Inside Base, the virtual function vfunc() is declared. When vfunc() is redefined by Derived1 andDerived2, the keyword virtual is not needed. However, it is not an error to include it when redefining
a virtual function inside a derived class. Inside main(), four variables (p, b, d1, d2) are declared.
Next, p is assigned the address of b, and vfunc() is called via p. Base version of vfunc() is executed.
Next, p is set to the address of d1, and again vfunc() is called by using p. This time p points to an
object of type Derived1. This causes Derived1::vfunc() to be executed. Finally, p is assigned the
address of d2, and p−>vfunc() causes the version of vfunc() redefined inside Derived2 to be
executed.
a virtual function inside a derived class. Inside main(), four variables (p, b, d1, d2) are declared.
Next, p is assigned the address of b, and vfunc() is called via p. Base version of vfunc() is executed.
Next, p is set to the address of d1, and again vfunc() is called by using p. This time p points to an
object of type Derived1. This causes Derived1::vfunc() to be executed. Finally, p is assigned the
address of d2, and p−>vfunc() causes the version of vfunc() redefined inside Derived2 to be
executed.
Note : Although you can call a virtual function in the “normal” manner by using an object’s name and
the dot operator, it is only when access is through a base-class pointer (or reference) that run-time
polymorphism is achieved. For example, assuming the preceding example, this is syntactically valid:
d2.vfunc(); // calls Derived2′s vfunc()
Calling a virtual function in this manner is not wrong, it simply does not take advantage of the virtual
nature of vfunc() .
Virtual Function and Multilevel Inheritance
the dot operator, it is only when access is through a base-class pointer (or reference) that run-time
polymorphism is achieved. For example, assuming the preceding example, this is syntactically valid:
d2.vfunc(); // calls Derived2′s vfunc()
Calling a virtual function in this manner is not wrong, it simply does not take advantage of the virtual
nature of vfunc() .
Virtual Function and Multilevel Inheritance
- When a Derived class that has inherited a virtual function is itself used as a base class for another
derived class, the virtual function can still be overridden. No matter how many times a virtual
function is inherited, it remains virtual.
function is inherited, it remains virtual.
- For example, consider this program:
#include <iostream>
using namespace std;
class Base {
public:
virtual void vfunc() { cout << “This is Base’s vfunc().\n”; }
};
class Derived1 : public Base {
public:
void vfunc() { cout << “This is Derived1′s vfunc().\n”; }
};
class Derived2 : public Derived1 {
public:
void vfunc() { cout << “This is Derived2′s vfunc().\n”; } // vfunc() is still virtual
};
int main()
{
Base *p, b;
Derived1 d1;
Derived2 d2;
p = &b; // point to Base
p->vfunc(); // access Base’s vfunc()
p = &d1; // point to Derived1
p->vfunc(); // access Derived1′s vfunc()
p = &d2; // point to Derived2
p->vfunc(); // access Derived2′s vfunc()
return 0;
}
As expected, the preceding program displays this output:
This is Base’s vfunc().
This is Derived1′s vfunc().
This is Derived2′s vfunc().
In this case, Derived2 inherits Derived1 rather than Base, but vfunc() is still virtual.
Note : When a derived class fails to override a virtual function, the first redefinition found in reverse order of derivation is used. For example, in the following program, Derived2 is derived from Derived1, which is derived from Base.
using namespace std;
class Base {
public:
virtual void vfunc() { cout << “This is Base’s vfunc().\n”; }
};
class Derived1 : public Base {
public:
void vfunc() { cout << “This is Derived1′s vfunc().\n”; }
};
class Derived2 : public Derived1 {
public:
void vfunc() { cout << “This is Derived2′s vfunc().\n”; } // vfunc() is still virtual
};
int main()
{
Base *p, b;
Derived1 d1;
Derived2 d2;
p = &b; // point to Base
p->vfunc(); // access Base’s vfunc()
p = &d1; // point to Derived1
p->vfunc(); // access Derived1′s vfunc()
p = &d2; // point to Derived2
p->vfunc(); // access Derived2′s vfunc()
return 0;
}
As expected, the preceding program displays this output:
This is Base’s vfunc().
This is Derived1′s vfunc().
This is Derived2′s vfunc().
In this case, Derived2 inherits Derived1 rather than Base, but vfunc() is still virtual.
Note : When a derived class fails to override a virtual function, the first redefinition found in reverse order of derivation is used. For example, in the following program, Derived2 is derived from Derived1, which is derived from Base.
#include <iostream>
using namespace std;
class Base {
public:
virtual void vfunc() { cout << “This is Base’s vfunc().\n”; }
};
class Derived1 : public Base {
public:
void vfunc() { cout << “This is Derived1′s vfunc().\n”; }
};
class Derived2 : public Derived1 {
public:
/* vfunc() not overridden. Since Derived2 is Derived from Derived1, Derived1′s
vfunc() is used.*/
};
int main()
{
Base *p, b;
Derived1 d1;
Derived2 d2;
p = &b;
p->vfunc(); // access Base’s vfunc()
p = &d1;
p->vfunc(); // access Derived1′s vfunc()
p = &d2;
p->vfunc(); // use Derived1′s vfunc()
return 0;
}
The program displays the following:
This is Base’s vfunc().
This is Derived1′s vfunc().
This is Derived1′s vfunc().
using namespace std;
class Base {
public:
virtual void vfunc() { cout << “This is Base’s vfunc().\n”; }
};
class Derived1 : public Base {
public:
void vfunc() { cout << “This is Derived1′s vfunc().\n”; }
};
class Derived2 : public Derived1 {
public:
/* vfunc() not overridden. Since Derived2 is Derived from Derived1, Derived1′s
vfunc() is used.*/
};
int main()
{
Base *p, b;
Derived1 d1;
Derived2 d2;
p = &b;
p->vfunc(); // access Base’s vfunc()
p = &d1;
p->vfunc(); // access Derived1′s vfunc()
p = &d2;
p->vfunc(); // use Derived1′s vfunc()
return 0;
}
The program displays the following:
This is Base’s vfunc().
This is Derived1′s vfunc().
This is Derived1′s vfunc().
Note : In case of hierarchical inheritance, when a derived class fails to override a virtual function, then when an object of that derived class accesses that function, the function defined by the base class
is used. For example, consider this program in which Derived2 does not override vfunc() :
#include <iostream>
using namespace std;
class Base {
public:
virtual void vfunc() { cout << “This is Base’s vfunc().\n”; }
};
class Derived1 : public Base {
public:
void vfunc() { cout << “This is Derived1′s vfunc().\n”; }
};
class Derived2 : public Base {
public:
// vfunc() not overridden by Derived2, Base’s is used
};
int main()
{
Base *p, b;
Derived1 d1;
Derived2 d2;
p = &b;
p->vfunc(); // access Base’s vfunc()
p = &d1;
p->vfunc(); // access Derived1′s vfunc()
p = &d2;
p->vfunc(); // use Base’s vfunc()
return 0;
}
The program produces this output:
This is Base’s vfunc().
This is Derived1′s vfunc().
This is Base’s vfunc().
Because Derived2 does not override vfunc() , the function defined by Base is used when vfunc() is
referenced relative to objects of type Derived2.
is used. For example, consider this program in which Derived2 does not override vfunc() :
#include <iostream>
using namespace std;
class Base {
public:
virtual void vfunc() { cout << “This is Base’s vfunc().\n”; }
};
class Derived1 : public Base {
public:
void vfunc() { cout << “This is Derived1′s vfunc().\n”; }
};
class Derived2 : public Base {
public:
// vfunc() not overridden by Derived2, Base’s is used
};
int main()
{
Base *p, b;
Derived1 d1;
Derived2 d2;
p = &b;
p->vfunc(); // access Base’s vfunc()
p = &d1;
p->vfunc(); // access Derived1′s vfunc()
p = &d2;
p->vfunc(); // use Base’s vfunc()
return 0;
}
The program produces this output:
This is Base’s vfunc().
This is Derived1′s vfunc().
This is Base’s vfunc().
Because Derived2 does not override vfunc() , the function defined by Base is used when vfunc() is
referenced relative to objects of type Derived2.
Calling a Virtual Function through a Base Class Reference
- In the preceding example, a virtual function was called through a base-class pointer, but the
polymorphic nature of a virtual function is also available when called through a base-class
reference. A reference is an implicit pointer. Thus, a base-class reference can be used to refer to an
object of the base class or any object derived from that base. When a virtual function is called
through a base-class reference, the version of the function executed is determined by the object
being referred to at the time of the call.
reference. A reference is an implicit pointer. Thus, a base-class reference can be used to refer to an
object of the base class or any object derived from that base. When a virtual function is called
through a base-class reference, the version of the function executed is determined by the object
being referred to at the time of the call.
- /* Here, a base class reference is used to access a virtual function. */
#include <iostream>
using namespace std;
class Base {
public:
virtual void vfunc() {
cout << “This is Base’s vfunc().\n”;
}
};
class Derived1 : public Base {
public:
void vfunc() {
cout << “This is Derived1′s vfunc().\n”;
}
};
class Derived2 : public Base {
public:
void vfunc() {
cout << “This is Derived2′s vfunc().\n”;
}
};
// Use a Base class reference parameter.
void f(Base &r) {
r.vfunc();
}
int main()
{
Base b;
Derived1 d1;
Derived2 d2;
f(b); // pass a Base object to f()
f(d1); // pass a Derived1 object to f()
f(d2); // pass a Derived2 object to f()
return 0;
}
This program produces the same output as its preceding version. In this example, the function f()
defines a reference parameter of type Base. Inside main() , the function is called using objects of typeBase, Derived1, and Derived2. Inside f() , the specific version of vfunc() that is called is determined
by the type of object being referenced when the function is called.
using namespace std;
class Base {
public:
virtual void vfunc() {
cout << “This is Base’s vfunc().\n”;
}
};
class Derived1 : public Base {
public:
void vfunc() {
cout << “This is Derived1′s vfunc().\n”;
}
};
class Derived2 : public Base {
public:
void vfunc() {
cout << “This is Derived2′s vfunc().\n”;
}
};
// Use a Base class reference parameter.
void f(Base &r) {
r.vfunc();
}
int main()
{
Base b;
Derived1 d1;
Derived2 d2;
f(b); // pass a Base object to f()
f(d1); // pass a Derived1 object to f()
f(d2); // pass a Derived2 object to f()
return 0;
}
This program produces the same output as its preceding version. In this example, the function f()
defines a reference parameter of type Base. Inside main() , the function is called using objects of typeBase, Derived1, and Derived2. Inside f() , the specific version of vfunc() that is called is determined
by the type of object being referenced when the function is called.