Unit 4
Inheritance & Polymorphism
One of the most important concepts in object-oriented programming is that of inheritance. Inheritance allows us to define a class in terms of another class, which makes it easier to create and maintain an application. This also provides an opportunity to reuse the code functionality and fast implementation time.
When creating a class, instead of writing completely new data members and member functions, the programmer can designate that the new class should inherit the members of an existing class. This existing class is called the base class, and the new class is referred to as the derived class.
The idea of inheritance implements the is a relationship. For example, mammal IS-A animal, dog IS-A mammal hence dog IS-A animal as well and so on.
Base and Derived Classes
A class can be derived from more than one classes, which means it can inherit data and functions from multiple base classes. To define a derived class, we use a class derivation list to specify the base class(es). A class derivation list names one or more base classes and has the form −
Class derived-class: access-specifier base-class
Where access-specifier is one of public, protected, or private, and base-class is the name of a previously defined class. If the access-specifier is not used, then it is private by default.
Consider a base class Shape and its derived class Rectangle as follows −
#include <iostream>
Using namespace std;
// Base class
Class Shape {
Public:
Void setWidth(int w) {
Width = w;
}
Void setHeight(int h) {
Height = h;
}
Protected:
Int width;
Int height;
};
// Derived class
Class Rectangle: public Shape {
Public:
Int getArea() {
Return (width * height);
}
};
Int main(void) {
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
// Print the area of the object.
Cout << "Total area: " << Rect.getArea() << endl;
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Total area: 35
Access Control and Inheritance
A derived class can access all the non-private members of its base class. Thus base-class members that should not be accessible to the member functions of derived classes should be declared private in the base class.
We can summarize the different access types according to - who can access them in the following way −
Access | Public | Protected | Private |
Same class | Yes | Yes | Yes |
Derived classes | Yes | Yes | No |
Outside classes | Yes | No | No |
A derived class inherits all base class methods with the following exceptions −
- Constructors, destructors and copy constructors of the base class.
- Overloaded operators of the base class.
- The friend functions of the base class.
Type of Inheritance
When deriving a class from a base class, the base class may be inherited through public, protected or private inheritance. The type of inheritance is specified by the access-specifier as explained above.
We hardly use protected or private inheritance, but public inheritance is commonly used. While using different type of inheritance, following rules are applied −
- Public Inheritance − When deriving a class from a public base class, public members of the base class become public members of the derived class and protected members of the base class become protected members of the derived class. A base class's private members are never accessible directly from a derived class, but can be accessed through calls to the public and protected members of the base class.
- Protected Inheritance − When deriving from a protected base class, public and protected members of the base class become protected members of the derived class.
- Private Inheritance − When deriving from a private base class, public and protected members of the base class become private members of the derived class.
Multiple Inheritances
A C++ class can inherit members from more than one class and here is the extended syntax −
Class derived-class: access baseA, access baseB....
Where access is one of public, protected, or private and would be given for every base class and they will be separated by comma as shown above. Let us try the following example −
#include <iostream>
Using namespace std;
// Base class Shape
Class Shape {
Public:
Void setWidth(int w) {
Width = w;
}
Void setHeight(int h) {
Height = h;
}
Protected:
Int width;
Int height;
};
// Base class PaintCost
Class PaintCost {
Public:
Int getCost(int area) {
Return area * 70;
}
};
// Derived class
Class Rectangle: public Shape, public PaintCost {
Public:
Int getArea() {
Return (width * height);
}
};
Int main(void) {
Rectangle Rect;
Int area;
Rect.setWidth(5);
Rect.setHeight(7);
Area = Rect.getArea();
// Print the area of the object.
Cout << "Total area: " << Rect.getArea() << endl;
// Print the total cost of painting
Cout << "Total paint cost: $" << Rect.getCost(area) << endl;
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Total area: 35
Total paint cost: $2450
KEY TAKEAWAY
One of the most important concepts in object-oriented programming is that of inheritance. Inheritance allows us to define a class in terms of another class, which makes it easier to create and maintain an application. This also provides an opportunity to reuse the code functionality and fast implementation time.
When creating a class, instead of writing completely new data members and member functions, the programmer can designate that the new class should inherit the members of an existing class. This existing class is called the base class, and the new class is referred to as the derived class.
The idea of inheritance implements the is a relationship. For example, mammal IS-A animal, dog IS-A mammal hence dog IS-A animal as well and so on.
If a function is defined as a friend function in C++, then the protected and private data of a class can be accessed using the function.
By using the keyword friend compiler knows the given function is a friend function.
For accessing the data, the declaration of a friend function should be done inside the body of a class starting with the keyword friend.
Declaration of friend function in C++
- Class class_name
- {
- Friend data_type function_name(argument/s); // syntax of friend function.
- };
In the above declaration, the friend function is preceded by the keyword friend. The function can be defined anywhere in the program like a normal C++ function. The function definition does not use either the keyword friend or scope resolution operator.
Characteristics of a Friend function:
- The function is not in the scope of the class to which it has been declared as a friend.
- It cannot be called using the object as it is not in the scope of that class.
- It can be invoked like a normal function without using the object.
- It cannot access the member names directly and has to use an object name and dot membership operator with the member name.
- It can be declared either in the private or the public part.
C++ friend function Example
Let's see the simple example of C++ friend function used to print the length of a box.
- #include <iostream>
- Using namespace std;
- Class Box
- {
- Private:
- Int length;
- Public:
- Box(): length(0) { }
- Friend int printLength(Box); //friend function
- };
- Int printLength(Box b)
- {
- b.length += 10;
- Return b.length;
- }
- Int main()
- {
- Box b;
- Cout<<"Length of box: "<< printLength(b)<<endl;
- Return 0;
- }
Output:
Length of box: 10
Let's see a simple example when the function is friendly to two classes.
- #include <iostream>
- Using namespace std;
- Class B; // forward declarartion.
- Class A
- {
- Int x;
- Public:
- Void setdata(int i)
- {
- x=i;
- }
- Friend void min(A,B); // friend function.
- };
- Class B
- {
- Int y;
- Public:
- Void setdata(int i)
- {
- y=i;
- }
- Friend void min(A,B); // friend function
- };
- Void min(A a,B b)
- {
- If(a.x<=b.y)
- Std::cout << a.x << std::endl;
- Else
- Std::cout << b.y << std::endl;
- }
- Int main()
- {
- A a;
- B b;
- a.setdata(10);
- b.setdata(20);
- Min(a,b);
- Return 0;
- }
Output:
10
In the above example, min() function is friendly to two classes, i.e., the min() function can access the private members of both the classes A and B.
C++ Friend class
A friend class can access both private and protected members of the class in which it has been declared as friend.
Let's see a simple example of a friend class.
- #include <iostream>
- Using namespace std;
- Class A
- {
- Int x =5;
- Friend class B; // friend class.
- };
- Class B
- {
- Public:
- Void display(A &a)
- {
- Cout<<"value of x is : "<<a.x;
- }
- };
- Int main()
- {
- A a;
- B b;
- b.display(a);
- Return 0;
- }
Output:
Value of x is : 5
In the above example, class B is declared as a friend inside the class A. Therefore, B is a friend of class A. Class B can access the private members of class A.
KEY TAKEAWAY
If a function is defined as a friend function in C++, then the protected and private data of a class can be accessed using the function.
By using the keyword friend compiler knows the given function is a friend function.
For accessing the data, the declaration of a friend function should be done inside the body of a class starting with the keyword friend.
Types of Inheritance
C++ supports five types of inheritance:
- Single inheritance
- Multiple inheritance
- Hierarchical inheritance
- Multilevel inheritance
- Hybrid inheritance
Derived Classes
A Derived class is defined as the class derived from the base class.
The Syntax of Derived class:
- Class derived_class_name :: visibility-mode base_class_name
- {
- // body of the derived class.
- }
Where,
Derived_class_name: It is the name of the derived class.
Visibility mode: The visibility mode specifies whether the features of the base class are publicly inherited or privately inherited. It can be public or private.
Base_class_name: It is the name of the base class.
- When the base class is privately inherited by the derived class, public members of the base class becomes the private members of the derived class. Therefore, the public members of the base class are not accessible by the objects of the derived class only by the member functions of the derived class.
- When the base class is publicly inherited by the derived class, public members of the base class also become the public members of the derived class. Therefore, the public members of the base class are accessible by the objects of the derived class as well as by the member functions of the base class.
Note:
- In C++, the default mode of visibility is private.
- The private members of the base class are never inherited.
C++ Single Inheritance
Single inheritance is defined as the inheritance in which a derived class is inherited from the only one base class.
Where 'A' is the base class, and 'B' is the derived class.
C++ Single Level Inheritance Example: Inheriting Fields
When one class inherits another class, it is known as single level inheritance. Let's see the example of single level inheritance which inherits the fields only.
- #include <iostream>
- Using namespace std;
- Class Account {
- Public:
- Float salary = 60000;
- };
- Class Programmer: public Account {
- Public:
- Float bonus = 5000;
- };
- Int main(void) {
- Programmer p1;
- Cout<<"Salary: "<<p1.salary<<endl;
- Cout<<"Bonus: "<<p1.bonus<<endl;
- Return 0;
- }
Output:
Salary: 60000
Bonus: 5000
In the above example, Employee is the base class and Programmer is the derived class.
C++ Single Level Inheritance Example: Inheriting Methods
Let's see another example of inheritance in C++ which inherits methods only.
- #include <iostream>
- Using namespace std;
- Class Animal {
- Public:
- Void eat() {
- Cout<<"Eating..."<<endl;
- }
- };
- Class Dog: public Animal
- {
- Public:
- Void bark(){
- Cout<<"Barking...";
- }
- };
- Int main(void) {
- Dog d1;
- d1.eat();
- d1.bark();
- Return 0;
- }
Output:
Eating...
Barking...
Let's see a simple example.
- #include <iostream>
- Using namespace std;
- Class A
- {
- Int a = 4;
- Int b = 5;
- Public:
- Int mul()
- {
- Int c = a*b;
- Return c;
- }
- };
- Class B : private A
- {
- Public:
- Void display()
- {
- Int result = mul();
- Std::cout <<"Multiplication of a and b is : "<<result<< std::endl;
- }
- };
- Int main()
- {
- B b;
- b.display();
- Return 0;
- }
Output:
Multiplication of a and b is : 20
In the above example, class A is privately inherited. Therefore, the mul() function of class 'A' cannot be accessed by the object of class B. It can only be accessed by the member function of class B.
How to make a Private Member Inheritable
The private member is not inheritable. If we modify the visibility mode by making it public, but this takes away the advantage of data hiding.
C++ introduces a third visibility modifier, i.e., protected. The member which is declared as protected will be accessible to all the member functions within the class as well as the class immediately derived from it.
Visibility modes can be classified into three categories:
- Public: When the member is declared as public, it is accessible to all the functions of the program.
- Private: When the member is declared as private, it is accessible within the class only.
- Protected: When the member is declared as protected, it is accessible within its own class as well as the class immediately derived from it.
Visibility of Inherited Members
Base class visibility | Derived class visibility | ||
Public | Private | Protected | |
Private | Not Inherited | Not Inherited | Not Inherited |
Protected | Protected | Private | Protected |
Public | Public | Private | Protected |
C++ Multilevel Inheritance
Multilevel inheritance is a process of deriving a class from another derived class.
C++ Multi Level Inheritance Example
When one class inherits another class which is further inherited by another class, it is known as multi level inheritance in C++. Inheritance is transitive so the last derived class acquires all the members of all its base classes.
Let's see the example of multi level inheritance in C++.
- #include <iostream>
- Using namespace std;
- Class Animal {
- Public:
- Void eat() {
- Cout<<"Eating..."<<endl;
- }
- };
- Class Dog: public Animal
- {
- Public:
- Void bark(){
- Cout<<"Barking..."<<endl;
- }
- };
- Class BabyDog: public Dog
- {
- Public:
- Void weep() {
- Cout<<"Weeping...";
- }
- };
- Int main(void) {
- BabyDog d1;
- d1.eat();
- d1.bark();
- d1.weep();
- Return 0;
- }
Output:
Eating...
Barking...
Weeping...
C++ Multiple Inheritance
Multiple inheritance is the process of deriving a new class that inherits the attributes from two or more classes.
Syntax of the Derived class:
- Class D : visibility B-1, visibility B-2, ?
- {
- // Body of the class;
- }
Let's see a simple example of multiple inheritance.
- #include <iostream>
- Using namespace std;
- Class A
- {
- Protected:
- Int a;
- Public:
- Void get_a(int n)
- {
- a = n;
- }
- };
- Class B
- {
- Protected:
- Int b;
- Public:
- Void get_b(int n)
- {
- b = n;
- }
- };
- Class C : public A,public B
- {
- Public:
- Void display()
- {
- Std::cout << "The value of a is : " <<a<< std::endl;
- Std::cout << "The value of b is : " <<b<< std::endl;
- Cout<<"Addition of a and b is : "<<a+b;
- }
- };
- Int main()
- {
- C c;
- c.get_a(10);
- c.get_b(20);
- c.display();
- Return 0;
- }
Output:
The value of a is : 10
The value of b is : 20
Addition of a and b is : 30
In the above example, class 'C' inherits two base classes 'A' and 'B' in a public mode.
Ambiguity Resolution in Inheritance
Ambiguity can be occurred in using the multiple inheritance when a function with the same name occurs in more than one base class.
Let's understand this through an example:
- #include <iostream>
- Using namespace std;
- Class A
- {
- Public:
- Void display()
- {
- Std::cout << "Class A" << std::endl;
- }
- };
- Class B
- {
- Public:
- Void display()
- {
- Std::cout << "Class B" << std::endl;
- }
- };
- Class C : public A, public B
- {
- Void view()
- {
- Display();
- }
- };
- Int main()
- {
- C c;
- c.display();
- Return 0;
- }
Output:
Error: reference to 'display' is ambiguous
Display();
- The above issue can be resolved by using the class resolution operator with the function. In the above example, the derived class code can be rewritten as:
- Class C : public A, public B
- {
- Void view()
- {
- A :: display(); // Calling the display() function of class A.
- B :: display(); // Calling the display() function of class B.
- }
- };
An ambiguity can also occur in single inheritance.
Consider the following situation:
- Class A
- {
- Public:
- Void display()
- {
- Cout<<?Class A?;
- }
- } ;
- Class B
- {
- Public:
- Void display()
- {
- Cout<<?Class B?;
- }
- } ;
In the above case, the function of the derived class overrides the method of the base class. Therefore, call to the display() function will simply call the function defined in the derived class. If we want to invoke the base class function, we can use the class resolution operator.
- Int main()
- {
- B b;
- b.display(); // Calling the display() function of B class.
- b.B :: display(); // Calling the display() function defined in B class.
- }
C++ Hybrid Inheritance
Hybrid inheritance is a combination of more than one type of inheritance.
Let's see a simple example:
- #include <iostream>
- Using namespace std;
- Class A
- {
- Protected:
- Int a;
- Public:
- Void get_a()
- {
- Std::cout << "Enter the value of 'a' : " << std::endl;
- Cin>>a;
- }
- };
- Class B : public A
- {
- Protected:
- Int b;
- Public:
- Void get_b()
- {
- Std::cout << "Enter the value of 'b' : " << std::endl;
- Cin>>b;
- }
- };
- Class C
- {
- Protected:
- Int c;
- Public:
- Void get_c()
- {
- Std::cout << "Enter the value of c is : " << std::endl;
- Cin>>c;
- }
- };
- Class D : public B, public C
- {
- Protected:
- Int d;
- Public:
- Void mul()
- {
- Get_a();
- Get_b();
- Get_c();
- Std::cout << "Multiplication of a,b,c is : " <<a*b*c<< std::endl;
- }
- };
- Int main()
- {
- D d;
- d.mul();
- Return 0;
- }
Output:
Enter the value of 'a' :
10
Enter the value of 'b' :
20
Enter the value of c is :
30
Multiplication of a,b,c is : 6000
C++ Hierarchical Inheritance
Hierarchical inheritance is defined as the process of deriving more than one class from a base class.
Syntax of Hierarchical inheritance:
- Class A
- {
- // body of the class A.
- }
- Class B : public A
- {
- // body of class B.
- }
- Class C : public A
- {
- // body of class C.
- }
- Class D : public A
- {
- // body of class D.
- }
Let's see a simple example:
- #include <iostream>
- Using namespace std;
- Class Shape // Declaration of base class.
- {
- Public:
- Int a;
- Int b;
- Void get_data(int n,int m)
- {
- a= n;
- b = m;
- }
- };
- Class Rectangle : public Shape // inheriting Shape class
- {
- Public:
- Int rect_area()
- {
- Int result = a*b;
- Return result;
- }
- };
- Class Triangle : public Shape // inheriting Shape class
- {
- Public:
- Int triangle_area()
- {
- Float result = 0.5*a*b;
- Return result;
- }
- };
- Int main()
- {
- Rectangle r;
- Triangle t;
- Int length,breadth,base,height;
- Std::cout << "Enter the length and breadth of a rectangle: " << std::endl;
- Cin>>length>>breadth;
- r.get_data(length,breadth);
- Int m = r.rect_area();
- Std::cout << "Area of the rectangle is : " <<m<< std::endl;
- Std::cout << "Enter the base and height of the triangle: " << std::endl;
- Cin>>base>>height;
- t.get_data(base,height);
- Float n = t.triangle_area();
- Std::cout <<"Area of the triangle is : " << n<<std::endl;
- Return 0;
- }
Output:
Enter the length and breadth of a rectangle:
23
20
Area of the rectangle is : 460
Enter the base and height of the triangle:
2
5
Area of the triangle is : 5
KEY TAKEAWAY
Types of Inheritance
C++ supports five types of inheritance:
- Single inheritance
- Multiple inheritance
- Hierarchical inheritance
- Multilevel inheritance
- Hybrid inheritance
Data hiding is one of the important features of Object Oriented Programming which allows preventing the functions of a program to access directly the internal representation of a class type. The access restriction to the class members is specified by the labeled public, private, and protected sections within the class body. The keywords public, private, and protected are called access specifiers.
A class can have multiple public, protected, or private labeled sections. Each section remains in effect until either another section label or the closing right brace of the class body is seen. The default access for members and classes is private.
Class Base {
Public:
// public members go here
Protected:
// protected members go here
Private:
// private members go here
};
The public Members
A public member is accessible from anywhere outside the class but within a program. You can set and get the value of public variables without any member function as shown in the following example −
#include <iostream>
Using namespace std;
Class Line {
Public:
Double length;
Void setLength( double len );
Double getLength( void );
};
// Member functions definitions
Double Line::getLength(void) {
Return length ;
}
Void Line::setLength( double len) {
Length = len;
}
// Main function for the program
Int main() {
Line line;
// set line length
Line.setLength(6.0);
Cout << "Length of line : " << line.getLength() <<endl;
// set line length without member function
Line.length = 10.0; // OK: because length is public
Cout << "Length of line : " << line.length <<endl;
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Length of line : 6
Length of line : 10
The private Members
A private member variable or function cannot be accessed, or even viewed from outside the class. Only the class and friend functions can access private members.
By default all the members of a class would be private, for example in the following class width is a private member, which means until you label a member, it will be assumed a private member −
Class Box {
Double width;
Public:
Double length;
Void setWidth( double wid );
Double getWidth( void );
};
Practically, we define data in private section and related functions in public section so that they can be called from outside of the class as shown in the following program.
#include <iostream>
Using namespace std;
Class Box {
Public:
Double length;
Void setWidth( double wid );
Double getWidth( void );
Private:
Double width;
};
// Member functions definitions
Double Box::getWidth(void) {
Return width ;
}
Void Box::setWidth( double wid ) {
Width = wid;
}
// Main function for the program
Int main() {
Box box;
// set box length without member function
Box.length = 10.0; // OK: because length is public
Cout << "Length of box : " << box.length <<endl;
// set box width without member function
// box.width = 10.0; // Error: because width is private
Box.setWidth(10.0); // Use member function to set it.
Cout << "Width of box : " << box.getWidth() <<endl;
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Length of box : 10
Width of box : 10
The protected Members
A protected member variable or function is very similar to a private member but it provided one additional benefit that they can be accessed in child classes which are called derived classes.
You will learn derived classes and inheritance in next chapter. For now you can check following example where I have derived one child class SmallBox from a parent class Box.
Following example is similar to above example and here width member will be accessible by any member function of its derived class SmallBox.
#include <iostream>
Using namespace std;
Class Box {
Protected:
Double width;
};
Class SmallBox:Box { // SmallBox is the derived class.
Public:
Void setSmallWidth( double wid );
Double getSmallWidth( void );
};
// Member functions of child class
Double SmallBox::getSmallWidth(void) {
Return width ;
}
Void SmallBox::setSmallWidth( double wid ) {
Width = wid;
}
// Main function for the program
Int main() {
SmallBox box;
// set box width using member function
Box.setSmallWidth(5.0);
Cout << "Width of box : "<< box.getSmallWidth() << endl;
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Width of box : 5
KEY TAKEAWAY
Data hiding is one of the important features of Object Oriented Programming which allows preventing the functions of a program to access directly the internal representation of a class type. The access restriction to the class members is specified by the labelled public, private, and protected sections within the class body. The keywords public, private, and protected are called access specifiers.
A class can have multiple public, protected, or private labeled sections. Each section remains in effect until either another section label or the closing right brace of the class body is seen. The default access for members and classes is private.
There is no such thing as a static class in C++. The closest approximation is a class that only contains static data members and static methods.
Static data members in a class are shared by all the class objects as there is only one copy of them in the memory, regardless of the number of objects of the class. Static methods in a class can only access static data members, other static methods or any methods outside the class.
A program that demonstrates static data members and static methods in a class in C++ is given as follows.
Example
#include <iostream>
Using namespace std;
Class Example {
Public :
Static int a;
Static int func(int b) {
Cout << "Static member function called";
Cout << "\nThe value of b is: " << b;
}
};
Int Example::a=28;
Int main() {
Example obj;
Example::func(8);
Cout << "\nThe value of the static data member a is: " << obj.a;
Return 0;
}
Output
The output of the above program is as follows.
Static member function called
The value of b is: 8
The value of the static data member a is: 28
Now let us understand the above program.
In the class Example, a is static data member of data type int. The method func() is a static method that prints "Static member function called" and displays the value of b. The code snippet that shows this is as follows.
Class Example {
Public :
Static int a;
Static int func(int b) {
Cout << "Static member function called";
Cout << "\nThe value of b is: " << b;
}
};
Int Example::a = 28;
In the function main(), an object obj is created of class Example. The function func() is called by using the class name and scope resolution operator. Then the value of a is displayed. The code snippet that shows this is as follows.
Int main() {
Example obj;
Example::func(8);
Cout << "\nThe value of the static data member a is: " << obj.a;
Return 0;
}
KEY TAKEAWAY
There is no such thing as a static class in C++. The closest approximation is a class that only contains static data members and static methods.
Static data members in a class are shared by all the class objects as there is only one copy of them in the memory, regardless of the number of objects of the class. Static methods in a class can only access static data members, other static methods or any methods outside the class.
Virtual classes are primarily used during multiple inheritance. To avoid, multiple instances of the same class being taken to the same class which later causes ambiguity, virtual classes are used.
Example
#include <iostream>
Using namespace std;
Class A {
Public:
Int a;
A(){
a = 10;
}
};
Class B : public virtual A {
};
Class C : public virtual A {
};
Class D : public B, public C {
};
Int main(){
//creating class D object
D object;
Cout << "a = " << object.a << endl;
Return 0;
}
Output
a = 10
KEY TAKEAWAY
Virtual classes are primarily used during multiple inheritance. To avoid, multiple instances of the same class being taken to the same class which later causes ambiguity, virtual classes are used.
The term "Polymorphism" is the combination of "poly" + "morphs" which means many forms. It is a greek word. In object-oriented programming, we use 3 main concepts: inheritance, encapsulation, and polymorphism.
Real Life Example of Polymorphism
Let's consider a real-life example of polymorphism. A lady behaves like a teacher in a classroom, mother or daughter in a home and customer in a market. Here, a single person is behaving differently according to the situations.
There are two types of polymorphism in C++:
- Compile time polymorphism: The overloaded functions are invoked by matching the type and number of arguments. This information is available at the compile time and, therefore, compiler selects the appropriate function at the compile time. It is achieved by function overloading and operator overloading which is also known as static binding or early binding. Now, let's consider the case where function name and prototype is same.
- Class A // base class declaration.
- {
- Int a;
- Public:
- Void display()
- {
- Cout<< "Class A ";
- }
- };
- Class B : public A // derived class declaration.
- {
- Int b;
- Public:
- Void display()
- {
- Cout<<"Class B";
- }
- };
In the above case, the prototype of display() function is the same in both the base and derived class. Therefore, the static binding cannot be applied. It would be great if the appropriate function is selected at the run time. This is known as run time polymorphism.
- Run time polymorphism: Run time polymorphism is achieved when the object's method is invoked at the run time instead of compile time. It is achieved by method overriding which is also known as dynamic binding or late binding.
Differences b/w compile time and run time polymorphism.
Compile time polymorphism | Run time polymorphism |
The function to be invoked is known at the compile time. | The function to be invoked is known at the run time. |
It is also known as overloading, early binding and static binding. | It is also known as overriding, Dynamic binding and late binding. |
Overloading is a compile time polymorphism where more than one method is having the same name but with the different number of parameters or the type of the parameters. | Overriding is a run time polymorphism where more than one method is having the same name, number of parameters and the type of the parameters. |
It is achieved by function overloading and operator overloading. | It is achieved by virtual functions and pointers. |
It provides fast execution as it is known at the compile time. | It provides slow execution as it is known at the run time. |
It is less flexible as mainly all the things execute at the compile time. | It is more flexible as all the things execute at the run time. |
C++ Runtime Polymorphism Example
Let's see a simple example of run time polymorphism in C++.
// an example without the virtual keyword.
- #include <iostream>
- Using namespace std;
- Class Animal {
- Public:
- Void eat(){
- Cout<<"Eating...";
- }
- };
- Class Dog: public Animal
- {
- Public:
- Void eat()
- { cout<<"Eating bread...";
- }
- };
- Int main(void) {
- Dog d = Dog();
- d.eat();
- Return 0;
- }
Output:
Eating bread...
C++ Run time Polymorphism Example: By using two derived class
Let's see another example of run time polymorphism in C++ where we are having two derived classes.
// an example with virtual keyword.
- #include <iostream>
- Using namespace std;
- Class Shape { // base class
- Public:
- Virtual void draw(){ // virtual function
- Cout<<"drawing..."<<endl;
- }
- };
- Class Rectangle: public Shape // inheriting Shape class.
- {
- Public:
- Void draw()
- {
- Cout<<"drawing rectangle..."<<endl;
- }
- };
- Class Circle: public Shape // inheriting Shape class.
- {
- Public:
- Void draw()
- {
- Cout<<"drawing circle..."<<endl;
- }
- };
- Int main(void) {
- Shape *s; // base class pointer.
- Shape sh; // base class object.
- Rectangle rec;
- Circle cir;
- s=&sh;
- s->draw();
- s=&rec;
- s->draw();
- s=?
- s->draw();
- }
Output:
Drawing...
Drawing rectangle...
Drawing circle...
Runtime Polymorphism with Data Members
Runtime Polymorphism can be achieved by data members in C++. Let's see an example where we are accessing the field by reference variable which refers to the instance of derived class.
- #include <iostream>
- Using namespace std;
- Class Animal { // base class declaration.
- Public:
- String color = "Black";
- };
- Class Dog: public Animal // inheriting Animal class.
- {
- Public:
- String color = "Grey";
- };
- Int main(void) {
- Animal d= Dog();
- Cout<<d.color;
- }
Output:
Black
KEY TAKEAWAY
The term "Polymorphism" is the combination of "poly" + "morphs" which means many forms. It is a greek word. In object-oriented programming, we use 3 main concepts: inheritance, encapsulation, and polymorphism.
Real Life Example of Polymorphism
Let's consider a real-life example of polymorphism. A lady behaves like a teacher in a classroom, mother or daughter in a home and customer in a market. Here, a single person is behaving differently according to the situations.
In these notes I am going to develop a C++ program to solve a problem based on a final exam problem from CMSC 250. The problem is to construct a simple system to manage trades in an electronic marketplace. In this problem traders will submit requests for trades to a central clearing house. For simplicity, we will assume that only one commodity trades in this market and that prices are not taken into account. Traders will submit requests to buy or sell a certain number of units of the commodity in question, and the system simply has to match buyers with sellers and produce a list of trades.
In the program I will describe in these notes the buy and sell orders will come in in the form of a text file containing a list of buy and sell orders. Each order includes the id number of the trade who placed the order and the number of units they want to buy or sell. Here is an example of a typical input file:
Buy 105 250
Sell 333 100
Sell 409 500
Buy 632 400
Sell 375 100
The system will match the buyers and sellers and emit a second text file with a list of all the trades that should be made. Here is the output file that goes along with the input example above:
333 sells 100 units to 105
409 sells 150 units to 105
409 sells 350 units to 632
375 sells 50 units to 632
Source code for the project
The order class
The first step in constructing a solution is to construct a simple class in C++ to represent orders. This class stores the name of the trader and number of units they want to trade. The order class does not contain information about whether this is an order to buy or sell - the system will record that information by putting buy and sell orders on separate lists.
Here is the C++ class declaration for the order class:
Class Order {
Private:
Std::string trader;
Int quantity;
Public:
Order(std::string name, int amount) {
Trader = name;
Quantity = amount;
}
Std::string getName() { return trader; }
Int getAmount() { return quantity; }
Void reduceAmount(int amt) {
Quantity -= amt;
}
};
This class declaration appears in a C++ header file, Order.h.
Creating and working with objects
Once we have constructed a class declaration we are in a position to start working with Order objects. C++ has two distinct systems for creating and interacting with objects. In the first system, we create objects as ordinary variables. Here is an example:
Order example("Joe",200);
Example.reduceAmount(100);
In this approach, we use a special syntax to declare and initialize the Order object named example. The syntax in the first statement above simultaneously declares a variable example of type Order and also invokes the constructor for the Order class to initialize that object with a trader name and a quantity. Once we have initialize the object we can interact with it by calling methods on the object using the dot method call syntax.
The second system for working with objects makes use of a pointer variable and the new operator:
Order *example = new Order("Joe",200);
Example->reduceAmount(100);
The first statement above has two parts: a variable declaration and an initialization. The variable declaration part,
Order *example;
Declares a variable named example whose type is "pointer to Order". A pointer is a type of variable that carries location information. In this case, the example variable will store the address of an Order object that we want to interact with. We initialize the pointer variable by using the C++ new operator to construct a new object of type Order. The new operator returns a pointer to the newly created object as a side effect of making the object for us.
Example = new Order("Joe",200);
Once we have created an object and obtained a pointer to it, we will interact with the object via the pointer variable. Pointers support one important operation, dereferencing, via the C++ dereference operator, *. Dereferencing a pointer to an object gives us access to the object itself, so we can call its methods using the usual dot notation:
(*example).reduceAmount(100);
Because calling a method on an object accessed via a pointer is a such a common scenario in C++, the language provides a more convenient syntactical equivalent. The expression
Example->reduceAmount(100);
Is a more convenient equivalent form.
Finally, when we are done working with an object that we created with new, we need to make the object disappear by using the delete operator.
Delete example;
This will free up the space that the object occupies, making that space available for other purposes in the program.
A queue class
The next thing we are going to need in our program is some way to make a list of buy and sell order objects. For this purpose I am going to introduce another class that will implement a simple linear data structure that can store orders.
The class is a queue class. A queue is a linear data structure that supports two operations, an enqueue operation that puts new items at the end of the queue, and a dequeue operation that removes items from the front of the queue.
A queue structure is typically implemented as a collection of nodes held together by pointers. The picture below illustrates this arrangement.
Each Node object contains some data (in this case a pointer to an Order object) and a pointer to the previous Node in the chain. The Queue object itself contains two pointers, a pointer to the Node at the head of the queue and a pointer to the Node at the tail of the queue.
Here now are the class declarations that set up the Queue and Node classes.
Template <typename T>
Class Node {
Public:
Node<T>* link;
T data;
Node(T item) { link = nullptr; data = item; }
T getData() { return data; }
};
Template <typename T>
Class Queue {
Private:
Node<T>* head;
Node<T>* tail;
Public:
Queue() { head = tail = nullptr; }
// Add a new item to the tail of the queue
Void enqueue(T item) {
If (head == nullptr) {
Head = tail = new Node<T>(item);
}
Else {
Node<T>* newNode = new Node<T>(item);
Tail->link = newNode;
Tail = newNode;
}
}
// Remove the item at the head of the queue
Void dequeue() {
If (head == nullptr)
Return;
Node<T>* toRemove = head;
Head = head->link;
If (head == nullptr)
Tail = nullptr;
Delete toRemove;
}
Bool isEmpty() {
Return head == nullptr;
}
// Return the data item at the front of the queue
T front() {
Return head->data;
}
};
These class declarations appear in a header file named Queue.h.
To make the Queue class more flexible, we make use of the C++ template mechanism to make both of these classes template classes. A template class contains placeholders for one or more types. In this case, we use a place holder T for the type of data stored in the queue nodes. To make a class a template class we place the notation
Template <typename T>
At the start of the class declaration.
When we instantiate a Queue, we use the template notation to specify what type should be used for the placeholder type T. In this application we will want to create Queue objects that store pointers to Order objects, so we will use the syntax
Queue<Order*> buyOrders;
To declare Queue objects with the T replaced by the concrete type Order*, which means "pointer to Order".
The Market class
With the creation of the Order and Queue classes we now have all of the elements in place to solve the market making problem. We now introduce a Market class that will be responsible for storing the orders and running the order matching algorithm.
Here is the class declaration for this Market class.
Class Market {
Private:
Queue<Order*> buyOrders;
Queue<Order*> sellOrders;
Public:
Market() {
}
Void addBuyOrder(std::string trader, int qty) {
BuyOrders.enqueue(new Order(trader, qty));
}
Void addSellOrder(std::string trader, int qty) {
SellOrders.enqueue(new Order(trader, qty));
}
Void clearTrades(std::string fileName);
};
This class declaration appears in a file named Market.h. Note that the class is missing code for one of its methods, the clearTrades method. In cases where a method's code would take up too much space in the class declaration, C++ allows us to simply list the method in the class declaration. The actual code for the method then appears in a separate file, Market.cpp. Here is the code for the missing method as it appears in that separate file:
Void Market::clearTrades(std::string fileName) {
Ofstream out;
Out.open(fileName);
While (!buyOrders.isEmpty() && !sellOrders.isEmpty()) {
Order* buy = buyOrders.front();
Order* sell = sellOrders.front();
Int toBuy = buy->getAmount();
Int toSell = sell->getAmount();
If (toBuy > toSell) {
Out << sell->getName() << " sells " << toSell << " units to " << buy->getName() << endl;
Buy->reduceAmount(toSell);
SellOrders.dequeue();
Delete sell;
}
Else if (toBuy == toSell) {
Out << sell->getName() << " sells " << toSell << " units to " << buy->getName() << endl;
BuyOrders.dequeue();
Delete buy;
SellOrders.dequeue();
Delete sell;
}
Else {
Out << sell->getName() << " sells " << toBuy << " units to " << buy->getName() << endl;
BuyOrders.dequeue();
Delete buy;
Sell->reduceAmount(toBuy);
}
}
Out.close();
}
The :: notation that appears at the start of the method code indicates that this is code for the clearTrades method in the Market class.
The main function
The last thing needed to make this example work is a main function. The code for that function appears in the file main.cpp:
Void main() {
Ifstream in;
In.open("orders.txt");
String kind, name;
Int qty;
Market market;
While (in >> kind >> name >> qty) {
If (kind == "buy")
Market.addBuyOrder(name, qty);
Else
Market.addSellOrder(name, qty);
}
In.close();
Market.clearTrades("trades.txt");
}
The code here opens the text file containing the order information and reads the orders. Each order has a kind, a trader name, and a quantity, so we can read the details of a single order by doing something like
In >> kind >> name >> qty
Since we don't know in advance how many orders are in the file, we execute the reading code in a while loop. We use a somewhat peculiar construct that uses the reading code as the test for a while loop. This approach takes advantage of the fact that the reading code not only reads data from the file: it also has a side effect. Evaluating the reading code returns information about whether or not the read was successful. If the read was successful, the reading expression evaluates to true, causing us to evaluate the code in the body of the loop. When we reach the end of the file and the read fails, the reading expression produces a side-effect value that evaluates to false, causing us to exit the while loop.
KEY TAKEAWAY
The first step in constructing a solution is to construct a simple class in C++ to represent orders. This class stores the name of the trader and number of units they want to trade. The order class does not contain information about whether this is an order to buy or sell - the system will record that information by putting buy and sell orders on separate lists.
C++ virtual function
- A C++ virtual function is a member function in the base class that you redefine in a derived class. It is declared using the virtual keyword.
- It is used to tell the compiler to perform dynamic linkage or late binding on the function.
- There is a necessity to use the single pointer to refer to all the objects of the different classes. So, we create the pointer to the base class that refers to all the derived objects. But, when base class pointer contains the address of the derived class object, always executes the base class function. This issue can only be resolved by using the 'virtual' function.
- A 'virtual' is a keyword preceding the normal declaration of a function.
- When the function is made virtual, C++ determines which function is to be invoked at the runtime based on the type of the object pointed by the base class pointer.
Late binding or Dynamic linkage
In late binding function call is resolved during runtime. Therefore compiler determines the type of object at runtime, and then binds the function call.
Rules of Virtual Function
- Virtual functions must be members of some class.
- Virtual functions cannot be static members.
- They are accessed through object pointers.
- They can be a friend of another class.
- A virtual function must be defined in the base class, even though it is not used.
- The prototypes of a virtual function of the base class and all the derived classes must be identical. If the two functions with the same name but different prototypes, C++ will consider them as the overloaded functions.
- We cannot have a virtual constructor, but we can have a virtual destructor
- Consider the situation when we don't use the virtual keyword.
- #include <iostream>
- Using namespace std;
- Class A
- {
- Int x=5;
- Public:
- Void display()
- {
- Std::cout << "Value of x is : " << x<<std::endl;
- }
- };
- Class B: public A
- {
- Int y = 10;
- Public:
- Void display()
- {
- Std::cout << "Value of y is : " <<y<< std::endl;
- }
- };
- Int main()
- {
- A *a;
- B b;
- a = &b;
- a->display();
- Return 0;
- }
Output:
Value of x is : 5
In the above example, * a is the base class pointer. The pointer can only access the base class members but not the members of the derived class. Although C++ permits the base pointer to point to any object derived from the base class, it cannot directly access the members of the derived class. Therefore, there is a need for virtual function which allows the base pointer to access the members of the derived class.
C++ virtual function Example
Let's see the simple example of C++ virtual function used to invoked the derived class in a program.
- #include <iostream>
- {
- Public:
- Virtual void display()
- {
- Cout << "Base class is invoked"<<endl;
- }
- };
- Class B:public A
- {
- Public:
- Void display()
- {
- Cout << "Derived Class is invoked"<<endl;
- }
- };
- Int main()
- {
- A* a; //pointer of base class
- B b; //object of derived class
- a = &b;
- a->display(); //Late Binding occurs
- }
Output:
Derived Class is invoked
Pure Virtual Function
- A virtual function is not used for performing any task. It only serves as a placeholder.
- When the function has no definition, such function is known as "do-nothing" function.
- The "do-nothing" function is known as a pure virtual function. A pure virtual function is a function declared in the base class that has no definition relative to the base class.
- A class containing the pure virtual function cannot be used to declare the objects of its own, such classes are known as abstract base classes.
- The main objective of the base class is to provide the traits to the derived classes and to create the base pointer used for achieving the runtime polymorphism.
Pure virtual function can be defined as:
- Virtual void display() = 0;
Let's see a simple example:
- #include <iostream>
- Using namespace std;
- Class Base
- {
- Public:
- Virtual void show() = 0;
- };
- Class Derived : public Base
- {
- Public:
- Void show()
- {
- Std::cout << "Derived class is derived from the base class." << std::endl;
- }
- };
- Int main()
- {
- Base *bptr;
- //Base b;
- Derived d;
- Bptr = &d;
- Bptr->show();
- Return 0;
- }
Output:
Derived class is derived from the base class.
In the above example, the base class contains the pure virtual function. Therefore, the base class is an abstract base class. We cannot create the object of the base class.
KEY TAKEAWAY
- A C++ virtual function is a member function in the base class that you redefine in a derived class. It is declared using the virtual keyword.
- It is used to tell the compiler to perform dynamic linkage or late binding on the function.
Pure Virtual Function
- A virtual function is not used for performing any task. It only serves as a placeholder.
- When the function has no definition, such function is known as "do-nothing" function.
- The "do-nothing" function is known as a pure virtual function. A pure virtual function is a function declared in the base class that has no definition relative to the base class.
An interface describes the behaviour or capabilities of a C++ class without committing to a particular implementation of that class.
The C++ interfaces are implemented using abstract classes and these abstract classes should not be confused with data abstraction which is a concept of keeping implementation details separate from associated data.
A class is made abstract by declaring at least one of its functions as pure virtual function. A pure virtual function is specified by placing "= 0" in its declaration as follows −
Class Box {
Public:
// pure virtual function
Virtual double getVolume() = 0;
Private:
Double length; // Length of a box
Double breadth; // Breadth of a box
Double height; // Height of a box
};
The purpose of an abstract class (often referred to as an ABC) is to provide an appropriate base class from which other classes can inherit. Abstract classes cannot be used to instantiate objects and serves only as an interface. Attempting to instantiate an object of an abstract class causes a compilation error.
Thus, if a subclass of an ABC needs to be instantiated, it has to implement each of the virtual functions, which means that it supports the interface declared by the ABC. Failure to override a pure virtual function in a derived class, then attempting to instantiate objects of that class, is a compilation error.
Classes that can be used to instantiate objects are called concrete classes.
Abstract Class Example
Consider the following example where parent class provides an interface to the base class to implement a function called getArea() −
#include <iostream>
Using namespace std;
// Base class
Class Shape {
Public:
// pure virtual function providing interface framework.
Virtual int getArea() = 0;
Void setWidth(int w) {
Width = w;
}
Void setHeight(int h) {
Height = h;
}
Protected:
Int width;
Int height;
};
// Derived classes
Class Rectangle: public Shape {
Public:
Int getArea() {
Return (width * height);
}
};
Class Triangle: public Shape {
Public:
Int getArea() {
Return (width * height)/2;
}
};
Int main(void) {
Rectangle Rect;
Triangle Tri;
Rect.setWidth(5);
Rect.setHeight(7);
// Print the area of the object.
Cout << "Total Rectangle area: " << Rect.getArea() << endl;
Tri.setWidth(5);
Tri.setHeight(7);
// Print the area of the object.
Cout << "Total Triangle area: " << Tri.getArea() << endl;
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Total Rectangle area: 35
Total Triangle area: 17
You can see how an abstract class defined an interface in terms of getArea() and two other classes implemented same function but with different algorithm to calculate the area specific to the shape.
Designing Strategy
An object-oriented system might use an abstract base class to provide a common and standardized interface appropriate for all the external applications. Then, through inheritance from that abstract base class, derived classes are formed that operate similarly.
The capabilities (i.e., the public functions) offered by the external applications are provided as pure virtual functions in the abstract base class. The implementations of these pure virtual functions are provided in the derived classes that correspond to the specific types of the application.
This architecture also allows new applications to be added to a system easily, even after the system has been defined.
KEY TAKEAWAY
An interface describes the behaviour or capabilities of a C++ class without committing to a particular implementation of that class.
The C++ interfaces are implemented using abstract classes and these abstract classes should not be confused with data abstraction which is a concept of keeping implementation details separate from associated data.
The word polymorphism means having many forms. Typically, polymorphism occurs when there is a hierarchy of classes and they are related by inheritance.
C++ polymorphism means that a call to a member function will cause a different function to be executed depending on the type of object that invokes the function.
Consider the following example where a base class has been derived by other two classes −
#include <iostream>
Using namespace std;
Class Shape {
Protected:
Int width, height;
Public:
Shape( int a = 0, int b = 0){
Width = a;
Height = b;
}
Int area() {
Cout << "Parent class area :" <<endl;
Return 0;
}
};
Class Rectangle: public Shape {
Public:
Rectangle( int a = 0, int b = 0):Shape(a, b) { }
Int area () {
Cout << "Rectangle class area :" <<endl;
Return (width * height);
}
};
Class Triangle: public Shape {
Public:
Triangle( int a = 0, int b = 0):Shape(a, b) { }
Int area () {
Cout << "Triangle class area :" <<endl;
Return (width * height / 2);
}
};
// Main function for the program
Int main() {
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
// store the address of Rectangle
Shape = &rec;
// call rectangle area.
Shape->area();
// store the address of Triangle
Shape = &tri;
// call triangle area.
Shape->area();
Return 0;
}
When the above code is compiled and executed, it produces the following result −
Parent class area :
Parent class area :
The reason for the incorrect output is that the call of the function area() is being set once by the compiler as the version defined in the base class. This is called static resolution of the function call, or static linkage - the function call is fixed before the program is executed. This is also sometimes called early binding because the area() function is set during the compilation of the program.
But now, let's make a slight modification in our program and precede the declaration of area() in the Shape class with the keyword virtual so that it looks like this −
Class Shape {
Protected:
Int width, height;
Public:
Shape( int a = 0, int b = 0) {
Width = a;
Height = b;
}
Virtual int area() {
Cout << "Parent class area :" <<endl;
Return 0;
}
};
After this slight modification, when the previous example code is compiled and executed, it produces the following result −
Rectangle class area
Triangle class area
This time, the compiler looks at the contents of the pointer instead of it's type. Hence, since addresses of objects of tri and rec classes are stored in *shape the respective area() function is called.
As you can see, each of the child classes has a separate implementation for the function area(). This is how polymorphism is generally used. You have different classes with a function of the same name, and even the same parameters, but with different implementations.
Virtual Function
A virtual function is a function in a base class that is declared using the keyword virtual. Defining in a base class a virtual function, with another version in a derived class, signals to the compiler that we don't want static linkage for this function.
What we do want is the selection of the function to be called at any given point in the program to be based on the kind of object for which it is called. This sort of operation is referred to as dynamic linkage, or late binding.
Pure Virtual Functions
It is possible that you want to include a virtual function in a base class so that it may be redefined in a derived class to suit the objects of that class, but that there is no meaningful definition you could give for the function in the base class.
We can change the virtual function area() in the base class to the following −
Class Shape {
Protected:
Int width, height;
Public:
Shape(int a = 0, int b = 0) {
Width = a;
Height = b;
}
// pure virtual function
Virtual int area() = 0;
};
The = 0 tells the compiler that the function has no body and above virtual function will be called pure virtual function.
KEY TAKEAWAY
The word polymorphism means having many forms. Typically, polymorphism occurs when there is a hierarchy of classes and they are related by inheritance.
C++ polymorphism means that a call to a member function will cause a different function to be executed depending on the type of object that invokes the function.
Deleting a derived class object using a pointer to a base class, the base class should be defined with a virtual destructor.
Example Code
#include<iostream>
Using namespace std;
Class b {
Public:
b() {
Cout<<"Constructing base \n";
}
Virtual ~b() {
Cout<<"Destructing base \n";
}
};
Class d: public b {
Public:
d() {
Cout<<"Constructing derived \n";
}
~d() {
Cout<<"Destructing derived \n";
}
};
Int main(void) {
d *derived = new d();
b *bptr = derived;
Delete bptr;
Return 0;
}
Output
Constructing base
Constructing derived
Destructing derived
Destructing base
KEY TAKEAWAY
Deleting a derived class object using a pointer to a base class, the base class should be defined with a virtual destructor.
In this section we will see what is early binding and what is late binding in C++. The binding means the process of converting identifiers into addresses. For each variables and functions this binding is done. For functions it is matching the call with the right function definition by the compiler. The binding is done either at compile time or at runtime.
Early Binding
This is compile time polymorphism. Here it directly associates an address to the function call. For function overloading it is an example of early binding.
Example
#include<iostream>
Using namespace std;
Class Base {
Public:
Void display() {
Cout<<" In Base class" <<endl;
}
};
Class Derived: public Base {
Public:
Void display() {
Cout<<"In Derived class" << endl;
}
};
Int main(void) {
Base *base_pointer = new Derived;
Base_pointer->display();
Return 0;
}
Output
In Base class
Late Binding
This is run time polymorphism. In this type of binding the compiler adds code that identifies the object type at runtime then matches the call with the right function definition. This is achieved by using virtual function.
Example
#include<iostream>
Using namespace std;
Class Base {
Public:
Virtual void display() {
Cout<<"In Base class" << endl;
}
};
Class Derived: public Base {
Public:
Void display() {
Cout<<"In Derived class" <<endl;
}
};
Int main() {
Base *base_pointer = new Derived;
Base_pointer->display();
Return 0;
}
Output
In Derived class
KEY TAKEAWAY
Early Binding
This is compile time polymorphism. Here it directly associates an address to the function call. For function overloading it is an example of early binding.
Late Binding
This is run time polymorphism. In this type of binding the compiler adds code that identifies the object type at runtime then matches the call with the right function definition. This is achieved by using virtual function.
We can create an object of one class into another and that object will be a member of the class. This type of relationship between classes is known as containership or has a relationship as one class contain the object of another class. And the class which contains the object and members of another class in this kind of relationship is called a container class.
The object that is part of another object is called contained object, whereas object that contains another object as its part or attribute is called container object.
Difference between containership and inheritance
Containership
-> When features of existing class are wanted inside your new class, but, not its interface
for eg->
1)computer system has a hard disk
2)car has an Engine, chassis, steering wheels.
Inheritance
-> When you want to force the new type to be the same type as the base class.
for eg->
1) Computer system is an electronic device
2) Car is a vehicle
Employees can be of Different types as can be seen above. It can be a developer, an HR manager, a sales executive, and so on. Each one of them belongs to Different problem domain but the basic Characteristics of an employee are common to all.
Syntax for Containership:
// Class that is to be contained
Class first {
.
.
};
// Container class
Class second {
// creating object of first
First f;
.
.
};
Below examples explain the Containership in C++ in a better way.
Example 1:
// CPP program to illustrate // concept of Containership
#include <iostream> Using namespace std;
Class first { Public: Void showf() { Cout << "Hello from first class\n"; } };
// Container class Class second { // creating object of first First f;
Public: // constructor Second() { // calling function of first class f.showf(); } };
Int main() { // creating object of second Second s; } |
Output:
Hello from first class
Explanation: In the class second we have an object of class first. This is another type of inheritance we are witnessing. This type of inheritance is known as has_a relationship as we say that class second has an object of first class first as its member. From the object f we call the function of class first.
Example 2:
#include <iostream> Using namespace std;
Class first { Public: First() { Cout << "Hello from first class\n"; } };
// Container class Class second { // creating object of first First f;
Public: // constructor Second() { Cout << "Hello from second class\n"; } };
Int main() { // creating object of second Second s; } |
Output:
Hello from first class
Hello from second class
Explanation: In this program we have not inherited class first into class second but as we are having an object of class first as a member of class second. So when default constructor of class second is called, due to presence of object f of first class in second, default constructor of class first is called first and then default constructor of class second is called .
Example 3:
#include <iostream> Using namespace std;
Class first { Private: Int num;
Public: Void showf() { Cout << "Hello from first class\n"; Cout << "num = " << num << endl; }
Int& getnum() { Return num; } };
// Container class Class second { // creating object of first First f;
Public: // constructor Second() { f.getnum() = 20; f.showf(); } };
Int main() { // creating object of second Second s; } |
Output:
Hello from first class
Num = 20
Explanation: With the help of containership we can only use public member/function of the class but not protected or private. In the first class we have returned the reference with the help of getnum. Then we show it by a call to showf.
Example 4
#include<iostream> Using namespace std;
Class cDate { Int mDay,mMonth,mYear; Public: CDate() { MDay = 10; MMonth = 11; MYear = 1999; } CDate(int d,int m ,int y) { MDay = d; MMonth = m; MYear = y; } Void display() { Cout << "day" << mDay << endl; Cout <<"Month" << mMonth << endl; Cout << "Year" << mYear << endl; } }; // Container class Class cEmployee { Protected: Int mId; Int mBasicSal; // Contained Object CDate mBdate; Public: CEmployee() { MId = 1; MBasicSal = 10000; MBdate = cDate(); } CEmployee(int, int, int, int, int); Void display(); };
CEmployee :: cEmployee(int i, int sal, int d, int m, int y) { MId = i; MBasicSal = sal; MBdate = cDate(d,m,y); } Void cEmployee::display() { Cout << "Id : " << mId << endl; Cout << "Salary :" <<mBasicSal << endl; MBdate.display(); }
Int main() { // Default constructor call CEmployee e1; e1.display(); // Parameterized constructor called CEmployee e2(2,20000,11,11,1999); e2.display(); Return 0; } |
Output
Id : 1
Salary :10000
Day 10
Month 11
Year 1999
Id : 2
Salary :20000
Day 11
Month 11
Year 1999
KEY TAKEAWAY
We can create an object of one class into another and that object will be a member of the class. This type of relationship between classes is known as containership or has a relationship as one class contain the object of another class. And the class which contains the object and members of another class in this kind of relationship is called a container class.
The object that is part of another object is called contained object, whereas object that contains another object as its part or attribute is called container object.
Singleton design pattern is a software design principle that is used to restrict the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. For example, if you are using a logger, that writes logs to a file, you can use a singleton class to create such a logger. You can create a singleton class using the following code −
Example
#include <iostream>
Using namespace std;
Class Singleton {
Static Singleton *instance;
Int data;
// Private constructor so that no objects can be created.
Singleton() {
Data = 0;
}
Public:
Static Singleton *getInstance() {
If (!instance)
Instance = new Singleton;
Return instance;
}
Int getData() {
Return this -> data;
}
Void setData(int data) {
This -> data = data;
}
};
//Initialize pointer to zero so that it can be initialized in first call to getInstance
Singleton *Singleton::instance = 0;
Int main(){
Singleton *s = s->getInstance();
Cout << s->getData() << endl;
s->setData(100);
Cout << s->getData() << endl;
Return 0;
}
Output
This will give the output −
0
100
KEY TAKEAWAY
Singleton design pattern is a software design principle that is used to restrict the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. For example, if you are using a logger, that writes logs to a file, you can use a singleton class to create such a logger.
References:
1. E Balagurusamy, “Programming with C++”, Tata McGraw Hill, 3rd Edition.
2. Herbert Schildt, “The Complete Reference C++”, 4th Edition.
3. Robert Lafore, “Object Oriented Programming in C++”, Sams Publishing, 4th Edition.
4. Matt Weisfeld, “The Object-Oriented Thought Process”, Pearson Education.