Polymorphism in Object Oriented Programming

You might also like

Download as pptx, pdf, or txt
Download as pptx, pdf, or txt
You are on page 1of 39

Polymorphism

Constructor Overloading
• In C++, We can have more than one constructor in a class with same name, as
long as each has a different list of arguments. This concept is known as
Constructor Overloading and is quite similar to function overloading. 
 
• Overloaded constructors essentially have the same name (exact name of the class)
and differ by number and type of arguments.
• A constructor is called depending upon the number and type of arguments passed.
• While creating the object, arguments must be passed to let compiler know, which
constructor needs to be called. 
// Constructor overloading
#include <iostream> void disp()
using namespace std; {
class construct cout<< area<< endl;
{ }
public: };
float area; int main()
// Constructor with no parameters {
construct() // Constructor Overloading
{ construct o;
area = 0; construct o2( 10, 20);
} o.disp();
// Constructor with two parameters o2.disp();
construct(int a, int b) return 1;
{ }
area = a * b;
}
// C++ program to demonstrate constructor age = a;
overloading
}
#include <iostream>
using namespace std;
int getAge() {
return age;
class Person {
}
private:
};
int age;

int main() {
public:
Person person1, person2(45);
// 1. Constructor with no arguments
Person() {
cout << "Person1 Age = " << person1.getAge()
age = 20; << endl;
} cout << "Person2 Age = " << person2.getAge()
<< endl;
// 2. Constructor with an argument
Person(int a) { return 0;
}
// C++ program to demonstrate constructor overloading // 3. Constructor with one argument
#include <iostream> Room(double len) {
using namespace std; length = len;
breadth = 7.2;
class Room { }
private: double calculateArea() {
double length; return length * breadth;
double breadth; }
public: };
// 1. Constructor with no arguments int main() {
Room() { Room room1, room2(8.2, 6.6), room3(8.2);
length = 6.9; cout << "When no argument is passed: " << endl;
breadth = 4.2; cout << "Area of room = " << room1.calculateArea() << endl;
} cout << "\nWhen (8.2, 6.6) is passed." << endl;
// 2. Constructor with two arguments cout << "Area of room = " << room2.calculateArea() << endl;
Room(double l, double b) { cout << "\nWhen breadth is fixed to 7.2 and (8.2) is passed:" <<
endl;
length = l;
cout << "Area of room = " << room3.calculateArea() << endl;
breadth = b;
return 0;
}
}
Operator Overloading
// Overload ++ when used as prefix and postfix
void display() {
#include <iostream> cout << "Count: " << value << endl;
using namespace std; }
class Count { };
private: int main() {
int value; Count count1;
public:
// Constructor to initialize count to 5 // Call the "void operator ++ (int)" function
Count() : value(5) {} count1++;
// Overload ++ when used as prefix count1.display();
void operator ++ () {
++value; // Call the "void operator ++ ()" function
} ++count1;
// Overload ++ when used as postfix
void operator ++ (int) { count1.display();
value++; return 0;
} }
// C++ program to overload the binary operator + // Overload the + operator int main() {
// This program adds two complex numbers Complex operator + (const Complex& obj) { Complex complex1, complex2, result;
#include <iostream> Complex temp;
using namespace std; temp.real = real + obj.real; cout << "Enter first complex number:\n";
class Complex { temp.imag = imag + obj.imag; complex1.input();
private: return temp;
float real; } cout << "Enter second complex number:\n";
float imag; complex2.input();
public: void output() {
// Constructor to initialize real and imag to 0 if (imag < 0) // complex1 calls the operator function
Complex() : real(0), imag(0) {} cout << "Output Complex number: " << real << // complex2 is passed as an argument to the
imag << "i"; function
void input() {
else result = complex1 + complex2;
cout << "Enter real and imaginary parts
respectively: "; cout << "Output Complex number: " << real << result.output();
"+" << imag << "i";
cin >> real;
}
cin >> imag; return 0;
};
} }
A Simple Example to Access Private Members of Other Class

#include <iostream>
using namespace std;
class Exmp_A{
    int i =3;
    // Declaring the friend class
    friend class Exmp_B;
};
class Exmp_B
{
  public:
    void display(Exmp_A &a)
    {
        cout<<"The value of i is : "<<a.i;
    }
};
int main(){
    Exmp_A a;
    Exmp_B b;
    b.display(a);
    return 0;
}
Output:
The value of i is : 3
As you can see in the output, Class Exmp_B could access the private data member i of class Exmp_A as it was a
friend class.

                  
#include <iostream>
using namespace std;
class Exmp_A{
    private:
        int x;
        // Declaring the friend class
        friend class Exmp_B;
    public:
        // Initializing x value using a constructor
        Exmp_A() : x(9) {}
};
class Exmp_B{
    private:
        int y;
    public:
        // Initializing y value using a constructor
        Exmp_B() : y(13) {}    
    // Function to perform addition
    int sum(){
        Exmp_A a;
        return a.x + y;
    }
};
int main()
{
    Exmp_B b;
    cout << "Sum is: " << b.sum();
    return 0;
}
Output:
Sum is : 22
#include <iostream>
using namespace std;
class Example{
    // A private member by default
    string s;
public:
        friend void show( Example value );
    void input( string val );
};
void Example::input( string val ){
    s = val;
}
void show( Example value ){
    cout<<"Value of private string data is : "<<value.s<<endl;
}
int main(){
    cout<<"Welcome to Simplilearn!"<<endl<<endl;
    Example value;
    value.input("Simplilearn");
    // Using friend function to display string
    show( value );
    return 0;
}
Output:
Welcome to Simplilearn!
Value of private string data is : Simplilearn
#include <iostream>
using namespace std;
class Example{
    private:
        int length;
    public:
        Example(): length(0) {}
        friend int printLength(Example); //friend function
};
int printLength(Example e){
    e.length = 20;
    return e.length;
}
int main(){
    Example e;
    cout<<"Length of Example box: "<< printLength(e)<<endl;
    return 0;
}
Output:
Length of Example box : 20
#include <iostream>
using namespace std;

class base {
public:
void display(int num) {
cout << "Num: " << num << endl;
}
void display(int num1, int num2 = 10) {
cout << "Num1: " << num1 << "Num2: " << num2 << endl;
}
void setValue(int num) {
cout << "int Num: " << num << endl;
}
void setValue(float num) {
cout << "Float Num: " << num << endl;
}
static void displayValue(int num) {
cout << "static int Num: " << num << endl;
}
static void displayValue(int num1, int num2) {
cout << "static Num: " << num1 << endl;
}
void displayInt(int num) {
cout << "static int Num: " << num << endl;
}
void displayInt(int& num) {
cout << "static Num: " << num << endl;
}
};

int main()
{
base b;
int i = 10;
b.display(10); //Error: Ambiguous, Reason: Since both the display candidates fits here
b.display(10, 20);
b.setValue(50);
b.setValue(10.0); //Error: Ambiguous, Reason: By default, this is double and double can be converted to float or int
b.setValue(10.0f);
b.displayValue(10);
b.displayInt(i); // Error: Ambiguous, Reason: Based on whether argument is caught with or without reference, does not result in overloading.
return 0;
}
For the following scenarios, function overloading is not possible
1) Difference in Return Type
      int getValue() {}
   void getValue() {}

The above two functions have same prototype except return type. Such kind of function cannot be overloaded.
2) Difference in constness of the parameter
void display(int num) {}
void display(const int num) {}
3) Difference in static keyword
void display(int num) {}
static void display(int num) {}
#include<iostream.h> };
class class-2; void exchange ( class-1 &x, class-2 &y)
class class-1{ {int temp=x. value 1;x. value I=y.valuo2; y.value2=temp;}
int value 1; int main( )
public: {class-1 c1
void indata( int a) ;class-2 c2
{ value=a; } ;c1.indata(l00);c2.indata(200);
void display(void) { cout<<value<<endl; } cout<<”values before exchange:”<<endl;
friend void exchange ( class-1 &, class-2 &); c1.display();
}; c2.display( );
class class-2 exchange (c1,c2);
{int value2; cout<<”values after exchange :”<<endl;
public: c1. display ();
void indata( int a) c2. display ( );
{ value2=a; } return(0);
void display(void) }
{ cout<<value2<<endl; } output:values before exchange 100 200values after
exchange 200100
friend void exchange(class-l & , class-2 &);
#include<iostream> sum(int a,int b);
class sum sum(int a, float d,double c);
{ };
Private: sum :: sum(int x,int y)
int a; int b; int c; float d; double e; {a=x;b=y;}
public: sum :: sum(int p, float q ,double r)
sum ( ) {a=p; d=q; e=r;}
{cout<<”enter a”; void main( )
cin>>a; { sum 1;sum m=sum(20,50);
cout<<”enter b”; sum n= sum(3,3.2,4.55);
cin>>b; }
cout<<”sum= “<<a+b<<endl; output: enter a : 3 enter b : 8 sum=11
} sum=70 sum=10.75
#include <iostream>
#include <cstring>
using namespace std;
int main () {
char str1[10] = "Hello";
char str2[10] = "World";
char str3[10];
int len ;
// copy str1 into str3
strcpy( str3, str1);
cout << "strcpy( str3, str1) : " << str3 << endl;
// concatenates str1 and str2
strcat( str1, str2);
cout << "strcat( str1, str2): " << str1 << endl;
// total lenghth of str1 after concatenation
len = strlen(str1);
cout << "strlen(str1) : " << len << endl;
Output
return 0;
• strcpy( str3, str1) : Hello
}
• strcat( str1, str2): HelloWorld
• strlen(str1) : 10
#include <iostream>

#include <string>

using namespace std;

int main () {

string str1 = "Hello";

string str2 = "World";

string str3;

int len ;

// copy str1 into str3

str3 = str1;

cout << "str3 : " << str3 << endl;

// concatenates str1 and str2

str3 = str1 + str2;

cout << "str1 + str2 : " << str3 << endl;

// total length of str3 after concatenation

len = str3.size(); Output

cout << "str3.size() : " << len << endl; • str3 : Hello

return 0; • str1 + str2 : HelloWorld

}
• str3.size() :  10
Virtual Functions
• As mentioned earlier, polymorphism refers to the property by which objects belonging to
different classes are able to respond to the same message, but in different forms.
• An essential requirement of polymorphism is therefore the ability to refer to objects without
any regard to their classes.
• This necessitates the use of a single pointer variable to refer to the objects of different classes.
• Here, we use the pointer to base class to refer to all the derived objects.
• But, we just discovered that a base pointer, even when it is made to contain the address of a
derived class, always executes the function in the base class.
• The compiler simply ignores the contents of the pointer and chooses the member function that
matches the type of the pointer.
• How do we then achieve polymorphism?
• It is achieved using what is known as 'virtual functions’.
• When we use the same function name in both the base and derived
classes, the function in base class is declared as virtual using the
keyword virtual preceding its normal declaration.
• When a function is made virtual, C++ determines which function to
use at run time based on the type of object pointed to by the base
pointer, rather than the type of the pointer.
• Thus, by making the base pointer to point to different objects, we can
execute different versions of the virtual function.
This program illustrates as follows
#include <iostream {
using namespace std; Base B;
class Base Derived D;
{ Base *bptr;
public: cout << “\n bptr points to Base \n “;
void display() {cout << "\n Display base";} bptr = &B;
virtual void show() {cout << "\n show base":} bptr -> display(); // calls Base version
}; bptr -> show(); // calls Base version
class Derived : public Base cout << "\n\n bptr points to Derived\n";
{ bptr = &D;
public: bptr -> display(); // calls Base version
void display() {cout << "\n Display derived“;} bptr -> show() ; //calls Derived version
void show() {cout << "\n show derived“;} return 0;
}; •} The output of Program would be:

int main() bptr points to Base


Display base
Show base
bptr points to Derived
Display base
Show derived
• Note: When bptr is made to point to the object D, the statement
bptr -> display();
calls only the function associated with the Base (i.e. Base :: display()),
whereas the statement
bptr -> show();
calls the Derived version of show().
• This is because the function display() has not been made virtual in the
Base class.
• One important point to remember is that, we must access
virtual functions through the use of a pointer declared as a
pointer to the base class. Why can't we use the object name
(with the dot operator) the same way as any other member
function to call the virtual functions?
• We can, but remember, run time polymorphism is achieved
only when a virtual function is accessed through a pointer to
the base class.
• Let us take an example where virtual functions are
implemented in practice. Consider a book shop which sells
both books and video-tapes. We can create a class known as
media that stores the title and price of a publication.
• We can then create two derived classes, one for storing the
number of pages in a book and another for storing the playing
time of a tape. Figure shows the class hierarchy for the book
shop.
• The classes are implemented in following program. A function
display() is used in all the classes to display the class contents.
• Notice that the function display() has been declared virtual in media,
the base class.
• In the main program we create a heterogeneous list of pointers of
type media as shown below:
• media *list[2] = { &book1, &tape1};
• The base pointers list[0] and list[1] are initialized with the addresses
of objects book1 and tape1 respectively.
RUNTIME POLYMORPHISM
void book :: display()
#include <iostream> class book: public media
{
#include <cstring> {
cout << "\n Title: " << title;
using namespace std; int pages:
cout << "\n Pages: " << pages;
class media public:
cout << "\n Price: " « price;
{ book(char *s, float a, int p): media(s,a)
}
protected: { void tape :: display()
char title[50]: pages =p; {
float price: } cout << "\n Title: " << title;
public: void display(); cout << "\n play time: « time << "mins";
media(char *s, float a) }; cout << "\n price: « price;
{ class tape : public media }
strcpy(title, s); {
price = a; float time;
} public:
virtual void display() { } // empty virtual function tape(char *s, float a, float t): media(s,
a)
};
{
time = t;
}
void display();
};
• The output of Program
int main() media *list[2]: would be:
char * title - new char[30]: list[0] =&book1; ENTER BOOK DETAILS
float price, time; list[1]=&tape1; Title: Programming_in_ANSI_C
int pages;
cout << “\n MEDIA DETAILS “; Price: 88
// Book details
cout << “\n......BOOK......"; Pages: 400
cout << "\n ENTER BOOK DETAILS\n";
list [0] -> display(); // display book details ENTER TAPE DETAILS
cout << "Title:"; cin >> title;
cout << "\n.....TAPE... “; Title: Computing_Concepts
cout << "Price:"; cin >> price;
cout << Pages: "; cin » pages; list[1] -> display(); // display tape details Price: 90

book book1(title, price, pages); return 0; Play time (mins): 55

// Tape details } MEDIA DETAILS


cout << "\n ENTER TAPE DETAILS\n"; ......BOOK......
cout << "Title: "; cin >> title; Title:Programming_in_ANSI_C
cout << "Price:"; cin>> price; Pages: 400
cout << " Play time (mins): ": cin>> time; Price: 88
tape tape1 (title, price, time);
..TAPE......
Title: Computing_Concepts
Play time: 55mins
Price: 90
Rules for Virtual Functions

1. The virtual functions must be members of some class.


2. They cannot be static members.
3. They are accessed by using object pointers,
4. A virtual function can be a friend of another class.
5. A virtual function in a base class must be defined, even though it may not be used.
6. The prototypes of the base class version of a virtual function and all the derived class versions must be identical. If
two functions with the same name have different prototypes, C++ considers them as overloaded functions, and the
virtual function mechanism is ignored.
7. We cannot have virtual constructors, but we can have virtual destructors.
8. While a base pointer can point to any type of the derived object, the reverse is not true. That is to say, we cannot
use a pointer to a derived class to access an object of the base type.
9. When a base pointer points to a derived class, incrementing or decrementing it will not make it to point to the
next object of the derived class. It is incremented or decremented only relative to its base type. Therefore, we should
not use this method to move the pointer to the next object.
10. If a virtual function is defined in the base class, it need not be necessarily redefined in the derived class. In such
cases, calls will invoke the base function.
Pure Virtual Functions
• It is normal practise to declare a function virtual inside the base class and
redefine it in the derived classes.
• The function inside the base class is seldom used for performing any task.
• It only serves as a placeholder.
• For example, we have not defined any object of class media and
therefore the function display() in the base class has been defined
‘empty’. Such functions are called “do-nothing” functions.
• A do nothing function may be defined as follows:
virtual void display()=0;
• Such functions are called pure virtual functions. A pure virtual function is a
function declared in a base class that has no definition relative to the base
class.
• In such cases, the compiler requires each derived class to either define the
function or redeclare it as a pure virtual function.
• Remember that a class containing a pure virtual function cannot be used to
declare any object of its own.
• As stated earlier, such classes ae called abstract base classes.
• The main objective of an abstract base class is to provide some traits to the
derived classes and to create a base pointer required for achieving run time
polymorphism.
Program is as follows:
#include<iostream> int main()
using namespace std; {
class B B* arra[2];
{
C e1;
public:
D e2;
virtual void example() = 0; //denotes pure virtual function definition
arra[0]=&e1;
};
class C: public B arra[1]=&e2;
{ arra[0]->example();
public: void example() { cout<<" Hello C++"<<endl; } arra[1]->example();
}; return 0;
class D: public B }
{
public: • Output
void example()
Hello C++
{
C++ has OOP
cout<<"C++ has OOP"<<endl;
}
};
Virtual Constructors and Destructors
• “A constructor can not be virtual”.
• There are some valid reasons that justify this statement.
• First, to create an object the constructor of the object class must be the
same type as the class.
• Second, at the time of calling a constructor , the virtual table would not
have been created to resolve any virtual function calls.
• Thus, a virtual constructor itself would not have anywhere to look up to.
• As a result, it is not possible to declare a constructor as virtual.
• A virtual destructor however, is pretty much feasible in C++.
• In fact, its use is often promoted in certain situations. One such
situation is when we need to make sure that the different destructors
in an inheritance hierarchy are called in order, particularly when the
base class pointer is referring to a derived type object.
• It must be noted here that the order of calling of destructors in an
inheritance hierarch is opposite to that of constructors.
Consider the following class declarations that make use of destructors:
class A
{
public:
~A()
{ //Base class destructor
}
};
class B : public A
{
public: ~B()
{ //Derived class destructor
}
};
main()
{
A *ptr = new B();
.
.
.
delete ptr;
}
• In the above class declarations, both base class A and the derived
class B have their own destructors .
• Now an A class pointer has been allocated a B class object.
• When this object pointer is deleted using the delete operator, it will
trigger the base class destructor and the derived class destructor
won’t be called at all.
• This may lead to a memory leak situation.
• To make sure that the derived class destructor is mandatorily called,
we must declare the base class destructor as virtual.
Declaring the base class destructor as virtual as follows:
class A
{
public:
virtual ~A()
{ //Base class destructor
}
};
.
.
.
C++ Function Overriding
• If base class and derived class have member functions with same
name and arguments. If you create an object of derived class and
write code to access that member function then, the member
function in derived class is only invoked, i.e., the member function of
derived class overrides the member function of base class. This
feature in C++ programming is known as function overriding.
Accessing the Overridden Function in Base Class From Derived Class

• To access the overridden function of base class from derived class,


scope resolution operator ::. For example: If you want to access
get_data() function of base class from derived class in above example
then, the following statement is used in derived class. A::get_data; //
Calling get_data() of class A. It is because, if the name of class is not
specified, the compiler thinks get_data() function is calling itself

You might also like