OOPS IN C++ Notes
OOPS IN C++ Notes
In the structured programming C, the user can create his own user-defined functions. The main function calls
the other functions. It indicates the execution of the program. When there is a function call, the control is
passed to that function. After completing the function, the control passes back to the main program. Moreover,
a variable inside a function is a local variable, and global variables are accessible by all the functions.
In addition to classes and objects, there are four major pillars in OOP. They are as follows.
Definition
Structured programming is a programming paradigm which divides the code into modules or function, while
OOP is a programming paradigm based on the concept of objects, which contain data in the form of fields
known as attributes, and code in the form of procedures known as methods. Thus, this explains the main
difference between structured and object oriented programming.
Main Focus
Furthermore, structured programming focuses on dividing the program into a set of functions in which each
function works as a subprogram while object oriented programming focuses on representing a program using a
set of objects which encapsulates data and object.
Modification
Moreover, it is difficult to modify the structured programs while it is easier to modify the Object Oriented
programs.
Communication
In structured programming, the main method communicates with the functions by calling those functions in the
main program whereas, in object oriented programming, the objects communicate with each other by passing
messages. Hence, this is an important difference between structured and object oriented programming.
Access Specifiers
There are no access specifiers in structured programming while there are access specifiers such as private,
public and protected in Object Oriented Programming. Thus, this is also an important difference between
structured and object oriented programming.
Security
Besides, data is not secure in structured programming, but it is secure in object oriented programming.
Code Reusability
Also, it is difficult to reuse code in structured programming, whereas it is easier to reuse code in object
oriented programming.
Object
Object is the physical as well as logical entity where as class is the only logical entity.
It is a basic unit of Object-Oriented Programming and represents the real-life entities. A C++ program
creates many objects which interact by invoking methods.
Class
Class: Class is a blue print which is containing only list of variables and method and no memory is
allocated for them. A class is a group of objects that has common properties.
A class is a user-defined blueprint or prototype from which objects are created. It represents the set of
properties or methods that are common to all objects of one type.
Encapsulation
Encapsulation is a process of wrapping of data and methods in a single unit is called encapsulation.
Encapsulation is achieved in C++ language by class concept. The main advantage of using of encapsulation
is to secure the data from other methods, when we make a data private then these data only use within the
class, but these data not accessible outside the class.
Abstraction
Abstraction is the concept of exposing only the required essential characteristics and behavior with respect
to a context.
Hiding of data is known as data abstraction. In object oriented programming language this is
implemented automatically while writing the code in the form of class and object.
Inheritance
The process of obtaining the data members and methods from one class to another class is known
as inheritance. It is one of the fundamental features of object-oriented programming.
Polymorphism
The process of representing one Form in multiple forms is known as Polymorphism. Here one form
represent original form or original method always resides in base class and multiple forms represents
overridden method which resides in derived classes.
Data Member
Method
Constructor
Block
Class and Interface
Object: Object is a instance of class, object has state and behaviors.
State
Behavior
Identity
Syntax
class_name object_reference;
Example
Employee e;
Identity: Object identity is typically implemented via a unique ID. The value of the ID is not visible to the
external user. But,it is used internally by the JVM to identify each object uniquely.
In real world many examples of object and class like dog, cat, and cow are belong to animal's class. Each
object has state and behaviors. For example a dog has state:- color, name, height, age as well as behaviors:-
barking, eating, and sleeping.
Vehicle class
Car, bike, truck these all are belongs to vehicle class. These Objects have also different different states and
behaviors. For Example car has state - color, name, model, speed, Mileage. as we;; as behaviors - distance
travel
Class Object
class Class_Name
{
data member;
method;
}
#include<iostream.h>
#include<conio.h>
class Employee
{
public:
int salary // data member
void sal()
{
cout<<"Enter salary: ";
cin>>salary;
cout<<"Salary: "<<salary;
}
};
void main()
{
clrscr();
Employee e; //creating an object of Employee
e.sal();
getch();
}
Output
Enter salary: 4500
Salary: 4500
Example
#include <iostream>
#include <string>
using namespace std;
class test
{
public:
string tutorial_name;
int id;
void printname();
void printid()
{
cout << "Tutorial id is: "<< id;
}
};
void test::printname()
{
cout << "Tutorial name is: " << tutorial_name;
}
int main() {
test t;
t.tutorial_name = "C++";
t.id = 1001;
t.printname();
cout << endl;
t.printid();
return 0;
}
The first method is called pass-by-value. Since a copy of the object is passed to the function, any changes
made to the object inside the function do not affect he object used to call the function.
The second method is called pass-by-reference. When an address of the object is passed, the called function
works directly on the actual object used in the call. This means that any changes made to the object inside the
function will reflect in the actual object. The pass-by-reference method is more efficient since it requires to
pass only the address of the object and not the entire object.
#include <iostream>
using namespace std;
class A {
public:
int n=100;
char ch='A';
void disp(A a){
cout<<a.n<<endl;
cout<<a.ch<<endl;
}
};
int main() {
A obj;
obj.disp(obj);
return 0;
}
Output:
100
A
return 0;
}
Output:
Name: Naeem
Id: 1001
Age: 29
The keywords public, private, and protected are called access specifiers. A class can have multiple public,
protected, or private labeled sections.
Note: By default, all members and function of a class is private i.e if no access specifier is specified.
class
{
private:
// private members and function
public:
// public members and function
protected:
// protected members and function
};
class Public_Access_Specifier
{
public: // public access specifier
int a; // Data Member Declaration
void display(); // Member Function declaration
}
class Private_Access_Specifier
{
private: // private access specifier
int a; // Data Member Declaration
void display(); // Member Function declaration
}
#include<iostream.h>
#include<conio.h>
class A
{
private:
int a;
public:
int b;
public:
void show()
{
a=10 ;
b=20;
clrscr();
//Every members can be access here, same class
cout<<"\nAccessing variable within the class"<<endl;
cout<<"Value of a: "<<a<<endl;
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 11
cout<<"Value of b: "<<b<<endl;
}
};
void main()
{
A obj; // create object
obj.show();
getch();
}
Note: If here, we access variable a inside main method it will give compile time error
Output
Syntax
class Protected_Access_Specifier
{
protected: // protected access specifier
int a; // Data Member Declaration
void display(); // Member Function Declaration
}
In below example all these access specifier public, private and protected.
Access Specifier Example in C++
#include<iostream.h>
#include<conio.h>
//using namespace std;
class Declaration
{
private:
int a;
public:
int b;
protected:
int c;
public:
void show()
{
a=10;
b=20;
c=30;
//Every members can be access here, same class
void main()
{
clrscr();
Declaration d; // create object
d.show();
cout<<"value of b: "<<d.b<<endl;
Output
Thus, with inline functions, the compiler does not have to jump to another location to execute the function,
and then jump back as the code of the called function is already available to the calling program.
Pros
The main advantage of inline function is it makes the program faster.
It speeds up your program by avoiding function calling overhead.
It save overhead of variables push/pop on the stack, when function calling happens.
It save overhead of return call from a function.
By marking it as inline, you can put a function definition in a header file (i.e. it can be included in
multiple compilation unit, without the linker complaining)
Cons
It increases the executable size due to code expansion.
C++ inlining is resolved at compile time. Which means if you change the code of the inlined
function, you would need to recompile all the code using it to make sure it will be updated
When used in a header, it makes your header file larger with information which users don’t care.
Syntax
inline function_name()
{
//function body
}
Example
#include<iostream.h>
#include<conio.h>
void main()
{
show(); // Call it like a normal function
getch();
}
Output
Hello word
Syntax
class class_Name
{
Returntype method()
{
...........
...........
}
Returntype method(datatype1 variable1)
{
...........
...........
}
Returntype method(datatype1 variable1, datatype2 variable2)
{
...........
...........
}
};
#include<iostream.h>
#include<conio.h>
class Addition
{
public:
void sum(int a, int b)
{
cout<<a+b;
}
void sum(int a, int b, int c)
{
cout<<a+b+c;
}
};
void main()
{
clrscr();
Addition obj;
obj.sum(10, 20);
cout<<endl;
obj.sum(10, 20, 30);
}
Output
30
60
#include<iostream.h>
#include<conio.h>
class Addition
{
public:
void sum(int a, int b)
{
cout<<a+b;
}
void sum(float a, float b)
{
cout<<a+b+c;
}
};
void main()
{
clrscr();
Addition obj;
obj.sum(10, 20);
cout<<endl;
obj.sum(10, 20, 30);
}
Output
30
25.25
class class_name
{
......
friend returntype function_name(arguments);
}
In below example you can access private function of class employee by using friend function.
Example
#include<iostream.h>
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 18
#include<conio.h>
class employee
{
private:
friend void sal();
};
void sal()
{
int salary=4000;
cout<<"Salary: "<<salary;
}
void main()
{
employee e;
sal();
getch();
}
Output
Salary: 4000
Example
#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
Friend class
Similarly like, friend function a class can be made a friend of another class using keyword friend. Similar
to friend functions, a friend class is a class whose members have access to the private or protected members of
another class:
When we create a friend class then all the member functions of the friend class also become the friend of the
other class. This requires the condition that the friend becoming class must be first declared or defined
(forward declaration).
Syntax
class A
{
friend class B; // class B is a friend class
......
}
class B
{
......
}
When a class is made a friend class, all the member functions of that class becomes friend function.
If B is declared friend class of A then, all member functions of class B can access private and protected
data of class A but, member functions of class A cannot private and protected data of class B.
Forward Declaration refers to the beforehand declaration of the syntax or signature of an identifier,
variable, function, class, etc. prior to its usage (done later in the program)
In C++, Forward declarations are usually used for Classes. In this, the class is pre-defined before its use so that
it can be called and used by other classes that are defined before this.
Example:
// Forward Declaration class A
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 20
class A;
// Definition of class A
class A{
// Body
};
Example without using forward declaration
#include <iostream>
using namespace std;
class B {
public:
int x;
void getdata(int n)
{
x = n;
}
friend int sum(A, B);
};
class A {
public:
int y;
void getdata(int m)
{
y = m;
}
friend int sum(A, B);
};
int sum(A m, B n)
{
int result;
result = m.y + n.x;
return result;
}
int main()
{
B b;
A a;
a.getdata(5);
b.getdata(4);
cout << "The sum is : " << sum(a, b);
return 0;
}
Output:
Compile Errors :
prog.cpp:14:18: error: 'A' has not been declared
friend int sum(A, B);
Explanation: Here the compiler throws this error because, in class B, the object of class A is being used,
which has no declaration till that line. Hence compiler couldn’t find class A.
Let us add the forward declaration to the above example and check the output again.
#include <iostream>
using namespace std;
// Forward declaration
class A;
class B;
class B {
int x;
public:
void getdata(int n)
{
x = n;
}
friend int sum(A, B);
};
class A {
int y;
public:
void getdata(int m)
{
y = m;
}
friend int sum(A, B);
};
int sum(A m, B n)
{
int result;
result = m.y + n.x;
return result;
}
int main()
{
B b;
A a;
a.getdata(5);
b.getdata(4);
cout << "The sum is : " << sum(a, b);
return 0;
}
Output:
The sum is : 9
The program runs without any errors now. A forward declaration tells the compiler about the existence of an
entity before actually defining the entity. Forward declarations can also be used with other entity in C++, such
as functions, variables and user-defined types.
Example
#include <iostream>
using namespace std;
// forward declaration
class ClassB;
class ClassA {
private:
int numA;
friend class ClassB; // friend class declaration
public:
ClassA() : numA(12) {} // constructor to initialize numA to 12
};
class ClassB {
private:
int numB;
public:
ClassB() : numB(1) {} // constructor to initialize numB to 1
int main() {
ClassB objectB;
cout << "Sum: " << objectB.add();
return 0;
}
Output
Sum: 13
Here, ClassB is a friend class of ClassA. So, ClassB has access to the members of classA. In ClassB, we
have created a function add() that returns the sum of numA and numB. Since ClassB is a friend class, we
can create objects of ClassA inside of ClassB
C++ Accessor & Mutator Member Functions
The accessor member function gets the value of a private data member of the class. This method is also known
as Get or Getter method.
//C++ Accessor Member Function
string getName()
{
return name; // return employees's name
}
class Student
{
private: // private data member
int rollno;
public:
// public function to get value of rollno - getter
int getRollno()
{
return rollno;
}
// public function to set value for rollno - setter
void setRollno(int i)
{
rollno=i;
}
};
int main()
{
Student A;
A.rollono=1; //Compile time error
cout<< A.rollno; //Compile time error
no return type.
Syntax
classname()
{
....
}
Note: If you do not specify a constructor, the compiler generates a default constructor for you (expects no
parameters and has an empty body).
Why use constructor?
The main use of constructor is placing user defined values in place of default values.
How Constructor eliminate default values?
Constructor are mainly used for eliminate default values by user defined values, whenever we create an
object of any class then its allocate memory for all the data members and initialize there default values. To
eliminate these default values by user defined values we use constructor.
Example of Constructor in C++
#include<iostream.h>
#include<conio.h>
class sum
{
int a,b,c;
sum()
{
a=10;
b=20;
c=a+b;
cout<<"Sum: "<<c;
}
};
void main()
{
sum s;
getch();
}
Output
Sum: 30
In above example when we create an object of "Sum" class then constructor of this class call and initialize
user defined value in a=10 and b=20. And here we no need to call sum() constructor.
Example
#include <iostream>
using namespace std;
// declare a class
class Wall {
private:
double length;
double height;
public:
// parameterized constructor to initialize variables
Wall(double len, double hgt) {
length = len;
height = hgt;
}
double calculateArea() {
return length * height;
}
};
int main() {
// create object and initialize data members
Wall wall1(10.5, 8.6);
Wall wall2(8.5, 6.3);
return 0;
}
Output
Example
Example
#include <iostream>
using namespace std;
// declare a class
class Wall {
private:
double length;
double height;
public:
// initialize variables with parameterized constructor
Wall(double len, double hgt) {
length = len;
height = hgt;
}
// copy constructor with a Wall object as parameter
// copies data of the obj parameter
Wall(Wall &obj) {
length = obj.length;
height = obj.height;
}
double calculateArea() {
return length * height;
}
};
int main() {
// create an object of Wall class
Wall wall1(10.5, 8.6);
// copy contents of wall1 to wall2
Wall wall2 = wall1;
// print areas of wall1 and wall2
cout << "Area of Wall 1: " << wall1.calculateArea() << endl;
cout << "Area of Wall 2: " << wall2.calculateArea();
return 0;
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 27
Output
In this program, we have used a copy constructor to copy the contents of one object of the Wall class to
another. The code of the copy constructor is:
Wall(Wall &obj) {
length = obj.length;
height = obj.height;
}
Notice that the parameter of this constructor has the address of an object of the Wall class.
We then assign the values of the variables of the obj object to the corresponding variables of the object calling
the copy constructor. This is how the contents of the object are copied.
In main(), we then create two objects wall1 and wall2 and then copy the contents of wall1 to wall2:
Here, the wall2 object calls its copy constructor by passing the address of the wall1 object as its argument
i.e. &obj = &wall1.
Constructor overloading in C++
As there is a concept of function overloading, similarly constructor overloading is applied. When we overload
a constructor more than a purpose it is called constructor overloading.
The declaration is the same as the class name but as they are constructors, there is no return type.
The criteria to overload a constructor are to differ the number of arguments or the type of arguments.
Example
Output
Person1 Age = 20
Person2 Age = 45
Destructor
Destructor is a member function which deletes an object. A destructor function is called automatically
when the object goes out of scope:
When destructor call
when program ends
when a block containing temporary variables ends
when a delete operator is called
Features of destructor
The same name as the class but is preceded by a tilde (~)
no arguments and return no values
Syntax
~classname()
{
......
}
Note: If you do not specify a destructor, the compiler generates a default destructor for you.
Example of Destructor in C++
#include<iostream.h>
#include<conio.h>
class sum
{
int a,b,c;
sum()
{
a=10;
b=20;
c=a+b;
cout<<"Sum: "<<c;
}
~sum()
{
cout<<<<endl;"call destructor";
}
delay(500);
};
void main()
{
sum s;
cout<<<<endl;"call main";
getch();
}
Output
Sum: 30
call main
call destructor
Explanation: In above example when you create object of class sum auto constructor of class is call and
after that control goes inside main and finally before end of program destructor is call.
Ob2 = Ob1;
Let us see the detailed differences between Copy constructor and Assignment Operator.
It is called when a new object is created from an This operator is called when an already initialized object
existing object, as a copy of the existing object is assigned a new value from another existing object.
It creates a separate memory block for the new It does not create a separate memory block or new
object. memory space.
#include <iostream>
#include <stdio.h>
using namespace std;
class Test {
public:
Test() {}
Test(const Test& t)
{
cout << "Copy constructor called " << endl;
}
// Driver code
int main()
{
Test t1, t2;
t2 = t1; //assignment operator
Test t3 = t1; // copy constructor
getchar();
return 0;
}
Output
Assignment operator called
Copy constructor called
Explanation: Here, t2 = t1; calls the assignment operator, same as t2.operator=(t1); and Test t3 =
t1; calls the copy constructor, same as Test t3(t1);
Shallow Copy
A shallow copy of an object copies the "main" object, but doesn’t copy the inner objects. The "inner objects"
are shared between the original object and its copy.
The problem with the shallow copy is that the two objects are not independent. If you modify the one object,
the change will be reflected in the other object.
Whenever we do not create our own user-defined copy constructor and we do copying, the compiler creates its
own hidden copy constructor.
Whenever this happens the member variables etc share the same memory locations.
Any change in object1 is also reflected in object2
This default copy constructor does shallow copy. The code below reflects what happens in shallow copy.
#include<iostream>
using namespace std;
class PrepInsta
{
int *var;
public:
// Parmeterized constructor
PrepInsta(int x) {
var = new int;
*var = x;
}
// setter
void setValue(int val){
*var = val;
}
// getter
int getValue(){
return *var;
}
};
int main()
{
// calling the normal constructor
PrepInsta obj1(10);
obj1.setValue(20);
cout << "\nAfter value change - " << endl;
// printing the values, assigned by constructors
cout << "obj1.var = " << obj1.getValue() << endl;
cout << "obj2.var = " << obj2.getValue() << endl;
// since we did shallow copy as we didnt make any
// user defined copy constructor
// change in obj1 is reflected in obj2
return 0; }
Output
Before value change -
obj1.var = 10
obj2.var = 10
Deep Copy
A deep copy is a fully independent copy of an object. If we copied our object, we would copy the entire object
structure. If you modify the one object, the change will not be reflected in the other object.
Whenever we create our own user-defined copy constructor and we do the copying.
Whenever this happens the member variables etc for any objects have their own memory locations.
For example, any change in object1 is not reflected in object2
User-defined copy constructor do a deep copy. Code below reflects what happens in shallow copy
#include<iostream>
using namespace std;
class PrepInsta
{
public:
int var;
PrepInsta(int x) {
var = x;
}
int main()
{
// calling the normal constructor
PrepInsta obj1(10);
obj1.var = 20;
return 0;
}
Output
Before value change -
obj1.var = 10
obj2.var = 10
Example
class Employee
{
int id;
static int count;
public:
void setData(void)
{
cout << "Enter the id" << endl;
cin >> id;
count++;
}
void getData(void)
{
cout << "The id of this employee is " << id << " and this is employee number " << count << endl;
}
int main()
{
Employee ali,javeria,zeeshan;
// ali.id = 1;
// ali.count=1; // cannot do this as id and count are private
ali.setData();
ali.getData();
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 36
Employee::getCount();
javeria.setData();
javeria.getData();
Employee::getCount();
zeeshan.setData();
zeeshan.getData();
Employee::getCount();
return 0;
}
Example Let's create another program to access the static member function using the class' object in the
C++ programming language.
#include <iostream>
using namespace std;
class Note
{
static int num; // declare a static data member
public:
static int func () // create static member function
{
cout << " The value of the num is: " << num << endl;
}
};
// initialize the static data member using the class name and the scope resolution operator
int Note :: num = 15;
int main ()
{
Note n; // create an object of the class Note
n.func(); // access static member function using the object
return 0;
}
Output
The value of the num is: 15
int main()
{
Test obj;
int x = 20;
obj.setX(x);
obj.print();
return 0;
}
Output
x = 20
}
};
int main()
{
Max one,two,three;
one.getdata();
two.getdata();
three=one.greater(two);
three.display();
return 0;
}
Output
Enter the Value :5
Enter the Value :8
Maximum No. is : 8
class Demo
{
public:
Demo FUN1()
{
cout <<"\nFUN1 CALLED"<<endl;
return *this;
}
Demo FUN2()
{
cout <<"\nFUN2 CALLED"<<endl;
return *this;
}
Demo FUN3()
{
cout <<"\nFUN3 CALLED"<<endl;
return *this;
}
};
int main()
{
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 39
Demo ob;
ob.FUN1().FUN2().FUN3();
return 0;
}
Output
FUN1 CALLED
FUN2 CALLED
FUN3 CALLED
Types of Overloading
There are two types of Overloading as follows:
1. Function Overloading.
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 40
2. Operator Overloading.
Operator overloading is a compile-time polymorphism in which the operator is overloaded to provide special
meaning to the user-defined data type. Operator overloading is used to overload or redefine most of the
operators available in C++. It is used to perform the operation on the user-defined data type.
C++ operator overloading is one of the most powerful features of C++ that allows a user to change the way
the operator works. In C++ the meaning of existing operator can be extended to operate on user-defined data
or class data.
Using operator overloading in C++, you can specify more than one meaning for an operator in one scope.
For example: - '+' operator can be overloaded to perform addition on various data types, like for Integer, String
(concatenation) etc.
Other example: - classes where arithmetic operators may be overloaded are Complex Number, Fractional
Number, Big Integer, etc
Why do we need Operator overloading in C++?
Operators in C++ like +, -, *, / can operate in datatypes like int, float, double etc as predefined operational
meanings. But these operators can’t operate in user-defined datatypes like objects without extension or adding
some sort of code to alter their operational meaning.
Such a way of extending the operational functionality of certain operators in C++ is called operator
overloading.
The advantage of operator overloading is that it can perform different operations on the same operand.
For Example:-
Suppose we have created three objects a1, a2 and sum from a class named Complex that represents complex
numbers.
Since operator overloading allows us to change how operators work, we can redefine how the + operator works
and use it to add the complex numbers of a1 and a2 by writing the following code:
sum = a1 + a2;
instead of something like
sum = a1.addNum(a2);
This makes our code intuitive and easy to understand.
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 41
Note: We cannot use operator overloading for fundamental data types like int, float, char and so on.
int main() {
Count c;
++c; // Call the "void operator ++ ()" function
c.display();
return 0; }
Output
The Count: 4
Note: When we overload operators, we can use it to work in any way we like. For example, we could have
used ++ to increase value by 100.
The above example works only when ++ is used as a prefix. To make ++ work as a postfix we use this syntax.
void operator ++ (int) {
// code
}
Notice the int inside the parentheses. It's the syntax used for using unary operators as postfix; it's not a function
parameter.
Example : C++ Operator Overloading using Unary Operator
// Overload ++ when used as prefix and postfix
#include <iostream>
using namespace std;
class Count {
private:
int value;
public:
Count() : value(5) {} // Constructor to initialize count to 5
void display() {
cout << "Count: " << value << endl;
}
};
int main() {
Count count1;
Output
Count: 6
Count: 7
The Example 2 works when ++ is used as both prefix and postfix. However, it doesn't work if we try to do
something like this:
Count count1, result;
// Error
result = ++count1;
This is because the return type of our operator function is void. We can solve this problem by
making Count as the return type of the operator function.
// return Count when ++ used as prefix
Count operator ++ () {
// code
}
// return Count when ++ used as postfix
Count operator ++ (int) {
// code
}
class Count {
private:
int value;
public :
Count() : value(5) {} // Constructor to initialize count to 5
return temp;
}
void display() {
cout << "Count: " << value << endl;
}
};
int main() {
Count count1, result;
result = ++count1; // Call the "Count operator ++ ()" function
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 44
result.display();
return 0;
}
Output
Count: 6
Count: 6
Here, we have used the following code for prefix operator overloading:
return temp;
}
The code for the postfix operator overloading is also similar. Notice that we have created an object temp and
returned its value to the operator function.
Also, notice the code
temp.value = ++value;
The variable value belongs to the count1 object in main() because count1 is calling the function,
while temp.value belongs to the temp object.
When we overload the binary operator for user-defined types by using the code:
n = s;
}
void operator + (A);
void display ();
};
void :: operator+ (A a) {
int m = n + a.n;
cout << "The Result of addition of two objects: " << m << endl;
}
int main() {
A a1(5);
A a2(4);
a1 + a2;
return 0;
}
Output
The Result of addition of two objects: 9
Example: C++ Program to overload the binary operator and adds two complex
numbers
#include <iostream>
using namespace std;
class Complex {
private:
float real;
float imagine;
public:
// Constructor to initialize real and imagine to 0
Complex() : real(0), imagine(0) {}
void input() {
cout << "Enter real and imaginary Number respectively: ";
cin >> real;
cin >> imagine;
}
void output() {
if (imagine < 0)
cout << "Output Complex number: " << real << imagine << "i";
else
cout << "Output Complex number: " << real << "+" << imagine << "i";
}
};
int main() {
Complex complex1, complex2, result;
return 0;
}
Output
Enter first complex number:
Enter real and imaginary parts respectively: 6 3
Enter second complex number:
Enter real and imaginary parts respectively: 5 4
Output Complex number: 11+7i
In this program, the operator function is:
using & makes our code efficient by referencing the complex2 object instead of making a duplicate
object inside the operator function.
using const is considered a good practice because it prevents the operator function from
modifying complex2.
Things to Remember in C++ Operator Overloading
1. With operator overloading, you can redefine the way an operator works only for the user-defined
types (objects, structures). You cannot use it for built-in types (float, char, int, etc.).
2. Two operators = and & are already overloaded by default in C++. For example, to copy objects of the
same class, we can directly use the = operator. We do not need to create an operator function.
3. Operator overloading cannot change the precedence and associativity of operators. However, if we
want to change the order of evaluation, parentheses should be used.
4. There are 5 operators that cannot be overloaded in C++. They are:
a. :: scope resolution operator
b. ?: ternary operator
c. . member selector
d. sizeof Operator
e. * member pointer selector
class Check
{
private:
int i;
public:
Check(): i(0) { }
void operator ++()
{ ++i; }
void Display()
{ cout << "i=" << i << endl; }
};
int main()
{
Check obj;
obj.Display(); // Displays the value of data member i for object obj
++obj; // Invokes operator function void operator ++( )
obj.Display(); // Displays the value of data member i for object obj
return 0;
}
Output
i=0
i=1
Working
Initially when the object obj is declared, the value of data member i for object obj is 0 (constructor
initializes i to 0).
When ++ operator is operated on obj, operator function void operator++( ) is invoked which increases the
value of data member i to 1.
This program is not complete in the sense that, you cannot used code:
obj1 = ++obj;
It is because the return type of operator function in above program is void. Here is the little modification of
above program so that you can use code obj1 = ++obj.
class Check
{
private:
int i;
public:
Check(): i(0) { }
void Display()
{ cout << "i = " << i << endl; }
};
int main()
{
Check obj, obj1;
obj.Display();
obj1.Display();
obj1 = ++obj;
obj.Display();
obj1.Display();
return 0;
}
Output
i=0
i=0
i=1
i=1
Working
This program is similar to the one above. The only difference is that, the return type of operator function
is Check in this case which allows to use both codes ++obj; obj1 = ++obj;. It is because, temp returned from
operator function is stored in object obj.
Since, the return type of operator function is Check, you can also assign the value of obj to another object.
Notice that, = (assignment operator) does not need to be overloaded because this operator is already
overloaded in C++ library.
Example: Program to Postfix Increment ++ Operator Overloading
Overloading of increment operator up to this point is only true if it is used in prefix form. This is the
modification of above program to make this work both for prefix form and postfix form.
#include <iostream>
using namespace std;
class Check
{
private:
int i;
public:
Check(): i(0) { }
Check operator ++ ()
{
Check temp;
temp.i = ++i;
return temp;
}
// Notice int inside barcket which indicates postfix increment.
Check operator ++ (int)
{
Check temp;
temp.i = i++;
return temp;
}
void Display()
{ cout << "i = "<< i <<endl; }
};
int main()
{
Check obj, obj1;
obj.Display();
obj1.Display();
// Operator function is called, only then value of obj is assigned to obj1
obj1 = ++obj;
obj.Display();
obj1.Display();
// Assigns value of obj to obj1, only then operator function is called.
obj1 = obj++;
obj.Display();
obj1.Display();
return 0;
}
Output
i=0
i=0
i=1
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 50
i=1
i=2
i=1
Working
When increment operator is overloaded in prefix form; Check operator ++ () is called but, when increment
operator is overloaded in postfix form; Check operator ++ (int) is invoked.
Notice, the int inside bracket. This int gives information to the compiler that it is the postfix version of
operator. Don't confuse this int doesn't indicate integer.
#include <iostream>
using namespace std;
class Check
{
private:
int i;
public:
Check(): i(3) { }
Check operator -- ()
{
Check temp;
temp.i = --i;
return temp;
}
// Notice int inside barcket which indicates postfix decrement.
Check operator -- (int)
{
Check temp;
temp.i = i--;
return temp;
}
void Display()
{ cout << "i = "<< i <<endl; }
};
int main()
{
Check obj, obj1;
obj.Display();
obj1.Display();
// Operator function is called, only then value of obj is assigned to obj1
obj1 = --obj;
obj.Display();
obj1.Display();
// Assigns value of obj to obj1, only then operator function is called.
obj1 = obj--;
obj.Display();
obj1.Display();
return 0;
}
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 51
Output
i=3
i=3
i=2
i=2
i=1
i=2
Overload binary plus (+) operator using non-member or free function in C++
This type of non-member function will access the private member of class. So the function must be friend type
(friend function).
using namespace std;
#include <iostream>
class Sample
{
//private data members
private:
int value;
public:
//default constructor
Sample()
{ value = 0;}
//Parameterized constructor
Sample(int c)
{ value = c;}
cout<<"S2 :"<<endl;
S2.printValue();
cout<<"S3 :"<<endl;
S3.printValue();
return 0;
}
Output
S1 :
Value is : 100
S2 :
Value is : 200
S3 :
Value is : 300
C++ Overload [] array subscript operator
We can overload the [] operator to implement safe array indexing. In C++, it is possible to overrun or underrun
an array boundary at run time without generating a run-time error message.
However, if you create a class that contains the array, and allow access to that array only through the
overloaded [] subscripting operator, then you can intercept an out-of-range index.
For example, the program adds a range check to the array subscripting operator:
// A safe array example.
#include <iostream>
#include <cstdlib>
using namespace std;
class My_Class {
int a[SIZE];
public:
My_Class() {
register int i;
{
if(i<0 || i> SIZE-1) {
cout << "\nIndex value of ";
cout << i << " is out-of-bounds.\n";
exit(1);
}
return a[i];
}
int main()
{
My_Class ob;
output.
2 25
Index value of 3 out of bounds
Inheritance in C++
The process of obtaining the data members and methods from one class to another class is known as inheritance. It is
one of the fundamental features of object-oriented programming.
Important points
In the inheritance the class which is give data members and methods is known as base or super or
parent class.
The class which is taking the data members and methods is known as sub or derived or child class.
Syntax
// methods
}
Real Life Example of Inheritance in C++
The real life example of inheritance is child and parents, all the properties of father are inherited by his son.
Diagram
In the above diagram data members and methods are represented in broken line are inherited from faculty class and
they are visible in student class logically.
Advantage of inheritance
If we develop any application using this concept than that application have following advantages,
Redundancy (repetition) of the code is reduced or minimized so that we get consistence results and
less storage cost.
Inheriting the feature from base class to derived class
In order to inherit the feature of base class into derived class we use the following syntax
Syntax
class classname-2 : classname-1
{
variable declaration;
method declaration;
}
Explanation
classname-1 and classname-2 represents name of the base and derived classes respectively.
: is operator which is used for inheriting the features of base class into derived class it improves the
// Base class
class Vehicle {
public:
string make = "Toyota";
void honk() {
cout << "Poom poom! \n" ;
}
};
// Derived class of Vehicle
class Car: public Vehicle {
public:
string model = "Corolla";
};
int main() {
Car car1;
car1.honk();
cout << car1.make + " " + car1.model;
return 0;
}
Here, the Car class derives from the Vehicle class. Since car derives from Vehicle, the member of Vehicle are accessible
to Car.
One thing you should observe is the use of the keyword public while inheriting Car from Vehicle.
The access modifier protected is especially relevant when it comes to C++ inheritance.
Like private members, protected members are inaccessible outside of the class. However, they can be accessed
by derived classes and friend classes/functions.
We need protected members if we want to hide the data of a class, but still want that data to be inherited by its
derived classes.
#include <iostream>
#include <string>
using namespace std;
// base class
class Animal {
private:
string color;
protected:
string type;
public:
void eat() {
cout << "I can eat!" << endl;
}
void sleep() {
cout << "I can sleep!" << endl;
}
void setColor(string clr) {
color = clr;
}
string getColor() {
return color;
}
};
// derived class
class Cat : public Animal {
public:
void setType(string t) {
type = t;
}
void displayInfo(string c) {
cout << "I am a " << type << endl;
cout << "My color is " << c << endl;
}
void meow() {
cout << "I can meow! meoww meoww!!" << endl;
}
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 57
};
int main() {
// Create object of the Cat class
Cat cat1;
// Calling members of the base class
cat1.eat();
cat1.sleep();
cat1.setColor("white");
// Calling member of the derived class
cat1.meow();
cat1.setType("mammal");
// Using getColor() of cat1 as argument
// getColor() returns string data
cat1.displayInfo(cat1.getColor());
return 0;
}
Output
I can eat!
I can sleep!
I can meow! meoww meoww!!
I am a mammal
My color is white
Here, the variable type is protected and is thus accessible from the derived class Cat. We can see this as we have
initialized type in the Cat class using the function setType().
On the other hand, the private variable color cannot be initialized in Cat.
public:
void setColor(string clr) {
// Error: member "Animal::color" is inaccessible
color = clr;
}
};
Also, since the protected keyword hides data, we cannot access type directly from an object of Cat or Animal class.
In C++ inheritance, we can derive a child class from the base class in different access modes. For example,
class Base {
.... ... ....
};
We have used the public keyword in order to inherit a class from a previously-existing base class. However, we can also
use the private and protected keywords to inherit classes.
For example,
class Animal {
// code
};
class Dog : private Animal {
// code
};
class Cat : protected Animal {
// code
};
The various ways we can derive classes are known as access modes. These access modes have the following effect:
1. Public mode: This is the most used inheritance mode. In this the protected member of super class becomes
protected members of sub class and public becomes public.
2. Protected mode: In protected mode, Then both public member and protected members of the Super class will
become protected in Sub class.
3. Private mode: In private mode, the protected and public members of super class become private members of
Sub class.
Note: private members of the base class are inaccessible to the derived class.
class Base {
public:
int a;
protected:
int b;
private:
int c;
};
class PublicDerived: public Base {
// a is public
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 59
// b is protected
// c is not accessible from PublicDerived
};
class Base {
private:
int pvt ;
protected:
int prot ;
public:
int pub ;
int main() {
PublicDerived object1;
Cout<<” Not Accessible “; // cout << "Private = " << object1.getPVT() << endl; //getPVT() is not accessible
cout << "Protected = " << object1.getProt() << endl;
cout << "Public = " << object1.pub << endl;
return 0;
}
Output
Not Accessible
Protected = 2
Public = 3
Here, we have derived PublicDerived from Base in public mode.
As a result, in PublicDerived:
prot is inherited as protected.
pub and getPVT() are inherited as public.
pvt is inaccessible since it is private in Base.
Since private and protected members are not accessible, we need to create public functions getPVT() and getProt() to
access them:
int getProt() {
return prot;
}
// function to access public member from Base
int getPub() {
return pub;
}
};
int main() {
ProtectedDerived object1;
cout << "Private cannot be accessed." << endl;
cout << "Protected = " << object1.getProt() << endl;
cout << "Public = " << object1.getPub() << endl;
return 0;
}
Output
As a result, in ProtectedDerived:
prot, pub and getPVT() are inherited as protected.
pvt is inaccessible since it is private in Base.
As we know, protected members cannot be accessed directly.
As a result, we cannot use getPVT() from ProtectedDerived. That is also why we need to create
the getPub() function in ProtectedDerived in order to access the pub variable.
private protected
Accessibility public members
members members
int main() {
PrivateDerived object1;
cout << "Private cannot be accessed." << endl;
cout << "Protected = " << object1.getProt() << endl;
cout << "Public = " << object1.getPub() << endl;
return 0;
}
Output
Private cannot be accessed.
Protected = 2
Public = 3
Here, we have derived PrivateDerived from Base in private mode.
As a result, in PrivateDerived:
prot, pub and getPVT() are inherited as private.
pvt is inaccessible since it is private in Base.
As we know, private members cannot be accessed directly.
As a result, we cannot use getPVT() from PrivateDerived. That is also why we need to create
the getPub() function in PrivateDerived in order to access the pub variable.
private
Accessibility protected members public members
members
Types of Inheritance
Based on number of ways inheriting the feature of base class into derived class it have five types they are:
Single inheritance
Multiple inheritance
Hierarchical inheritance
Multiple inheritance
Hybrid inheritance
Single inheritance
In single inheritance there exists single base class and single derived class.
class A
{
... .. ...
};
class B: public A
{
... .. ...
};
Here, class B is derived from the base class A.
int main(){
B obj;
obj.display();
return 0;
}
Output
Base class A content.
In this program, class B is derived from class A. The obj object of class B is defined in the main() function.
When the display() function is called, display() in class A is executed. It's because there is no display() function in
class B.
The function also doesn't exist in class B, so the compiler looks for it in class A (as B is derived from A).
Example of Inheritance in C++
#include<iostream.h>
#include<conio.h>
class employee
{
public:
int salary;
};
class developer : public employee
{
employee e;
public:
void salary()
{
cout<<"Enter employee salary: ";
cin>>e.salary; // access base class data member
cout<<"Employee salary: "<<e.salary;
}
};
void main()
{
clrscr();
developer obj;
obj.salary();
getch();
}
Output
Multilevel inheritances
In multiple inheritances there exists single base class, single derived class and multiple intermediate base classes.
Single base class + single derived class + multiple intermediate base classes.
Intermediate base classes
An intermediate base class is one in one context with access derived class and in another context same class access base
class.
Hence all the above three inheritance types are supported by both classes and interfaces.
In multilevel inheritance, the derived class inherits property from another derived class.
For example, class B inherits from class A and class C inherits property from class B.
class A
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 66
{
... .. ...
};
class B: public A
{
... .. ...
};
class C: public B
{
... ... ...
};
Here, class B is derived from the base class A and the class C is derived from the derived class B.
#include <iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout<<"This is a Animal.";
}
void display()
{
cout<<”Example of Multilevel inheritance”; }
};
int main()
{
Cow obj;
obj.display();
return 0;
}
Output
This is a Animal.
Objects with 4 legs are Animals.
Cow has 4 Legs.
Multiple inheritance
In multiple inheritance there exist multiple classes and single derived class.
For example, as explained below, class Derived inherits property from both Class Base1 and Class Base2.
class A
{
... .. ...
};
class B: public A
{
... .. ...
};
class C: public A, public B
{
... .. ...
};
In C++ programming, a class can be derived from more than one parents. For example: A class Parrot is derived from
base classes Animal and Bird. It makes sense because Parrot is a Animal as well as a Bird.
If you try to call the function using the object of the derived class, compiler shows error. It's because compiler doesn't
know which function to call. For example,
class base1
{
public:
void someFunction( )
{ .... ... .... }
};
class base2
{
void someFunction( )
{ .... ... .... }
};
class derived : public base1, public base2
{
};
int main()
{
derived obj;
obj.someFunction() // Error!
}
This problem can be solved using scope resolution function to specify which function to class either base1 or base2.
int main()
{
obj.base1::someFunction( ); // Function of base1 class is called
obj.base2::someFunction(); // Function of base2 class is called.
}
Hybrid inheritance
Combination of any inheritance type . In hierarchical inheritance, more than one sub class is inherited from a single base
class. In this, all features that are common in child classes are included in the base class
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 69
For example: Football, Cricket, Badminton are derived from Sports class .
};
// Second Sub class
class Cat : public Animal
{
};
int main(){
Cat obj1;
Dog obj;2
return 0;
}
Output
Example
#include <iostream>
using namespace std;
class MyBaseClass {
protected:
int x;
public:
MyBaseClass() {
x = 20;
}
friend void display();
};
class MyDerivedClass : public MyBaseClass {
private:
int y;
public:
MyDerivedClass() {
x = 40;
}
};
void display() {
MyDerivedClass derived;
cout << "The value of private member of Base class is: " << derived.x << endl;
cout << "The value of private member of Derived class is: " << derived.y << endl;
}
main() {
display();
}
Output
[Error] 'int MyDerivedClass::y' is private
[Error] within this context
Virtual base class in C++ OOP
What is the Diamond Problem?
When we inherit more than one base class in the same derived class and all these base classes also inherit another but same
single class (super parent), multiple references of the super parent class become available to the derived class.
So, it becomes unclear to the derived class, which version of the super parent class it should refer to.
1. Create class A
2. declare the variable “int number” inside class A.
3. Class C and Class B get the “int number” variable from class A.
4. Class D is getting the “int number” from both class B, and Class C. This is not good to do.
We must avoid this redundancy and never inherit the same member(e.g int number) from multiple parent classes .
The solution to solve the above-mentioned problem is to make the parent base class a virtual class.
Here, Class D is getting the “int number” directly from the parent base virtual class.
#include <iostream>
using namespace std;
class A {
public:
int number;
A() // constructor
{
number = 10;
}
};
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C{};
int main()
{
D object; // object is created for class d
cout << "number = " << object.number << endl;
return 0;
}
Output
number=10
Virtual Destructor
A virtual destructor is used to free up the memory space allocated by the derived class object or instance while deleting
instances of the derived class using a base class pointer object. A base or parent class destructor use the virtual keyword
that ensures both base class and the derived class destructor will be called at run time, but it called the derived class first
and then base class to release the space occupied by both destructors.
delete bptr; /* Here pointer object is called to delete the space occupied by the destructor.*/
}
Output:
As we can see in the above output, when the compiler compiles the code, it calls a pointer object in the main function that
refers to the base class. So, it executes the base class' constructor() function and then moves to the derived class'
constructor() function. After that, it deletes the pointer object occupied by the base class' destructor and the derived class'
destructor. The base class pointer only removes the base class's destructor without calling the derived class' destructor in
the program. Hence, it leaks the memory in the program.
Note: If the base class destructor does not use a virtual keyword, only the base class destructor will be called or deleted its
occupied space because the pointer object is pointing to the base class. So it does not call the derived class destructor to
free the memory used by the derived class, which leads to memory leak for the derived class.
int main()
{
Base *bptr = new Derived; // A pointer object reference the Base class.
delete bptr; // Delete the pointer object.
}
Output:
In the above program, we have used a virtual destructor inside the base class that calls the derived class' destructor before
calling the base class' destructor and release the spaces or resolve the memory leak issue in the program.
Abstraction in C++
Real Life Example of Abstraction in C++
Abstraction is the concept of exposing only the required essential characteristics and behavior with respect to a
context.
Hiding of data is known as data abstraction. In object oriented programming language this is implemented
automatically while writing the code in the form of class and object.
Abstraction is one of the feature of Object Oriented Programming, where you show only relevant details to the user
and hide irrelevant details. You can understand with this example, when you send an email to someone you just click
send and you get the success message, what actually happens when you click send, how data is transmitted over
network to the recipient is hidden from you (because it is irrelevant to you).
void main()
{
sum s;
s.add();
getch();
}
Output
Enter any two number:
4
5
Sum: 9
Note: Data abstraction can be used to provide security for the data from the unauthorized methods.
Note: Note: In C++ language data abstraction can be achieved by using class.
Encapsulation is not providing full security because we can access private member of the class using reflection API,
but in case of Abstraction we can't access static, abstract data member of a class.
Data abstraction increases the reusability of the code by avoiding any chances of redundancy.
It increases the readability of the code as it eliminates the possibility of displaying the complex
working of the code.
With the implementation of classes and objects, comes enhanced security. Since data abstraction is
a method of implementing classes and objects any denying access to other classes of accessing the
data members and member functions of the base class.
It helps the user to write a high-level code.
It separates the entire program into code and implementation making it more comprehensible.
Helps the user to avoid writing the low level code.
Avoids code duplication and increases reusability.
Can change internal implementation of class independently without affecting the user.
Helps to increase security of an application or program as only important details are provided to the
user.
Implementation of Abstraction
Below we discuss about some way to Implement Abstraction.
Abstraction using Classes: We can implement Abstraction in C++ using classes. Class helps us to group data
members and member functions using available access specifiers. A Class can decide which data member will be
visible to outside world and which is not.
Abstraction in Header files: One more type of abstraction in C++ can be header files. For example, consider the
pow() method present in math.h header file. Whenever we need to calculate power of a number, we simply call the
function pow() present in the math.h header file and pass the numbers as arguments without knowing the underlying
algorithm according to which the function is actually calculating power of numbers.
Abstraction using access specifiers: Access specifiers are the main pillar of implementing abstraction in C++. We
can use access specifiers to enforce restrictions on class members.
For example;
Members declared as public in a class, can be accessed from anywhere in the program.
Members declared as private in a class, can be accessed only from within the class. They are not
allowed to be accessed from any part of code outside the class.
In this case whatever programs are using these interfaces, they would not be impacted and would just need a
recompilation with the latest implementation.
Encapsulation in C++
Member functions;
};
main()
{
classname objectname1,objectname2……………;
}
The common example of encapsulation is Capsule. In capsule all medicine are encapsulated in side capsule.
Automatic Cola Vending Machine: Suppose you go to an automatic cola vending machine and request for a cola.
Here automatic cola vending machine is a class. It contains both data i.e. Cola can and operations i.e. service
mechanism and they are wrapped/integrated under a single unit Cola Vending Machine. This is called Encapsulation.
A Washing Machine and It's Power Button: What is the function that power button does? Switches the machine on
(obviously). But did u ever imagined the inside mechanism. Doesn't matter unless it's functioning well. That's
encapsulation. The object is wrapped and inner details are hidden. Only thing is that object can be called and used.
User friendly!
You can't access private date outside the class.
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 82
Benefits of encapsulation
Provides abstraction between an object and its clients.
Protects an object from unwanted access by clients.
Example: A bank application forbids a client to change an Account's balance.
Example of Encapsulation in C++
#include<iostream.h>
#include<conio.h>
class sum
{
private: int a,b,c;
public:
void add()
{
clrscr();
cout<<"Enter any two numbers: ";
cin>>a>>b;
c=a+b;
cout<<"Sum: "<<c;
}
};
void main()
{
sum s;
s.add();
getch();
}
Output
In above example all data and function are bind inside class sum.
The data members should be labeled as private using the private access specifiers.
The member function which manipulates the data members should be labeled as public using the
public access specifier.
Association
In object-oriented general software design, the relationship between one object's functionality and another's is known as an
association. Note that an association between two objects is not the same thing as inheritance between two classes.
Association means that one object uses another object or a function/method in that other object. In other words,
association is defined as the relationship between objects when one object has one or more references to other objects.
Inheritance implies that two objects are the same type of object. One object just happens to be either a more
generalized or more specific version of the other object. Association occurs between two different objects.
Inheritance is said to be an IS-A relationship whereas association is known as a HAS-A relationship.
The modality of inheritance depends on the programming language features. For example, Java does not support
multiple inheritance, but C++ does. On the other hand, any language can have one-to-one, one-to-many, and
many-to-many associations between objects.
There are two types of associations between objects: composition and aggregation.
C++ Composition
C++ Composition- In real-life complex objects are often built from smaller and simpler objects. For example, a car is
built using a metal frame, an engine some tires, a transmission system, a steering wheel, and a large number of other parts.
A personal computer is built from a CPU, a motherboard, memory unit, input and output units, etc. even human beings are
building from smaller parts such as a head, a body, legs, arms, and so on. This process of building complex objects from
simpler ones is called c++ composition. It is also known as object composition.
So far, all of the classes that we have used had member variables that are built-in data type (e.g. int, float, double, char).
While this is generally sufficient for designing and implementing small, simple classes, but it gradually becomes difficult
to carry out from more complex classes, especially for those built from many sub-parts. In order to facilitate the building
of complex classes from simpler ones, C++ allows us to do object C++ Composition in a very simple way by using classes
as member variables in other classes.
A class can have one or more objects of other classes as members. A class is written in such a way that the object of
another existing class becomes a member of the new class. this relationship between classes is known as C++
Composition. It is also known as containment, part-whole or has-a relationship. A common form of software reusability
is C++ Composition.
In C++ Composition, an object is a part of another object. The object that is a part of another object is known as a sub-
object. When a C++ Composition is destroyed, then all of its sub-objects are destroyed as well.
In other word In this type of relationship, parts of an object are completely dependent on it. This means that the objects
would not have any existence without the object. If we destroy the object, then these parts would immediately get
destroyed too. They make up an object and are vital for it. Here objects depend on each other. This means that the
existence of one is impossible without others
Example Such as when a car is destroyed, then its motor, frame, and other parts are also destroyed with it. It has a do and
die relationship.
Another example of real-life is Our Body and Blood. Blood resides inside a body. This means that blood is a part of the
body. Blood is useless if it is not inside a body. Blood present outside the body as no importance.
Program example: how to use C++ Composition in programming:
#include <iostream>
using namespace std;
class X
{
private:
int d;
public:
void set_value(int k)
{
d=k;
}
void show_sum(int n)
{
cout<<"sum of "<<d<<" and "<<n<<" = "<<d+n<<endl;
} };
class Y
{
public:
X a;
void print_result()
{
a.show_sum(5);
};
int main()
{
Y b;
b.a.set_value(20);
b.a.show_sum(100);
b.print_result();
Explanation:
In this program, class X has one data member ‘d’ and two member functions ‘set_value()’ and ‘show_sum()’. The
set_value() function is used to assign value to ‘d’. the show_sum() function uses an integer type parameter. It adds the
value of parameter with the value of ‘d’ and displays the result o the screen.
Another class Y is defined after the class x. the class Y has an object of class x that is the C++ Composition relationship
between classes x and y. this class has its own member function print_result().
In the main() function, an object ‘b’ of class y is created. The member function set_value() of object ‘a’ that is the sub-
object of object ‘b’ is called by using two dot operators. One dot operator is used to access the member of the object ‘b’
that is object ‘a’, and second is used to access the member function set_value() of sub-object ‘a’ and ‘d’ is assigned a
value 20.
In the same way, the show_sum() member function is called by using two dot operators. The value 100 is also passed as a
parameter. The member function print_result of object ‘b’ of class Y is also called for execution. In the body of this
function, the show_sum() function of object ‘a’ of class X is called for execution by passing value 5.
C++ Aggregation
Aggregation is a type of association that is used to represent the “HAS-A” relationship between two objects. This is a
subclass for a relation type association. Where the association is loosely coupled that exists between two classes where a
relation exists, but aggregation restricts some situations of associations. This type of relation is like a whole/Part
relationship type where one class owns another class thus one class object is a part of another class object. But the lifetime
of a part class does not depend on the lifetime of the whole class neither whole class can exist without an object of part
class.
For Example – There are two classes Address and Person, Each Person has an address.
Aggregation is a way to represent HAS-A relation between the objects of 2 individual classes.. It is a subtype of
association type of relation but more restrictive.
Class PartClass{
//instance variables
//instance methods
}
class Whole{
PartClass* partclass;
}
Explanation: In the above syntax, the Whole class represents the class that is a container class for other Part class that is
contained in the object of the whole class. Here each object of Whole class holds a reference pointer to the object of the
Part class.
For example – BUS HAS-A Engine. Here Bus is a container class. Part class is a class whose object is contained within
the objects of a container class. An object of the Part class can be referred in more than 1 object of Whole class and also a
lifetime of a contained class object does not depend on the lifetime of the existence of the object of a container class.
Here object of one class is referred to using a pointer variable present in the container class object.
Example: Person has Address is represented using two classes Person and Address. Since as we can see, Address has a
Person is meaningless thus Person is container class and Address is a class whose object is contained within the container
class object.
Here we can also see, the lifetime of an object of address class does not depend on the lifetime of the object of Person
class. Thus objects are not tightly coupled. Also, one address can easily be associated with more than a Person since more
than 1 person can live on the same address. Thus the object of address class can be associated with more than one object of
Person class.
In this way, one can easily establish HAS-A relation between objects of 2 classes.
Example of C++ Aggregation Let us try to illustrate the implementation of the aggregation type of relation
between 2 classes Person and address using a C++ program.
#include <iostream>
#include<string.h>
using namespace std;
class Address {
public:
int houseNo;
string colony,city, province;
Address(inthno, string colony, string city, string province)
{
this->houseNo = hno;
this->colony=colony;
this->city = city;
this->province = province;
}
};
class Person
{
private:
Address* address;
public:
string name;
Person(string name, Address* address)
{
this->name = name;
this->address = address;
}
void display()
{
cout<< name<< " "<< " "<< address->houseNo<<" "<<address-> colony<<" " <<address->city<< " "<<address-
>state<<endl;
}
};
int main(void) {
Address add1= Address(868 ,"housing Colony","Lahore","Punjab");
Person p1 = Person("Ali",&add1);
Person p2 = Person("Seema",&add1);
p1.display();
p2.display();
return 0;
}
Output:
Ali 868 housing colony Lahore Punjab
seema 868 housing colony Lahore punjab
Explanation: Here Person has instance variable name which tells the name of the person and a pointer variable to
address class object. Address class object has variables such as House, street, city, and province. Here we have 2 persons
Ali and Seema living on the same address thus share the same address object add1.
1. Aggregation helps establishing a relation between objects of 2 individual classes where one is Whole class and
the other is a part class. It is used to code ‘HAS-A’ relation between objects of two classes where once object of
the part class can be associated with more than 1 object of Whole class and does not have existence dependency
on it.
2. This type of relation is a special form of relation named as association where objects does not have any
dependency on each other and show bidirectional relation between objects of different classes.
3. Aggregation type of relation represents uni-directional relation between objects of 2 classes like car and garage
where car is a part of garage same car can be parked in any other garage as well. Thus defines its one direction
relation.
4. It also helps to improve the reusability of the code. Once objects have been created, any Whole class object is
capable of holding a reference to the object of any of the Part class.
5. It also helps to improve the readability of the code as the relation between 2 classes can be made more
understandable once it is in the form of HAS-A relation the same we interpret them in our day to day life. such as
Bus HAS-A Engine and Department HAS-A Teacher.
Polymorphism in C++
Real Life Example of Polymorphism in C++
The process of representing one Form in multiple forms is known as Polymorphism. Here one form represent original
form or original method always resides in base class and multiple forms represents overridden method which resides in
derived classes.
Polymorphism is derived from 2 greek words: poly and morphs. The word "poly" means many and morphs means
forms. So polymorphism means many forms.
Polymorphism is an important concept of object-oriented programming. It simply means more than one form. That is,
the same entity (function or operator) behaves differently in different scenarios
For example:- The + operator in C++ is used to perform two specific functions. When it is used with numbers
(integers and floating-point numbers), it performs addition.
int a = 5;
int b = 6;
int sum = a + b; // sum = 11
And when we use the + operator with strings, it performs string concatenation. For example,
Type of polymorphism
Compile time polymorphism: This type of polymorphism is also referred to as static binding or early
binding. It takes place during compilation. We use function overloading and operator overloading to achieve
compile-time polymorphism.
Run time polymorphism: Run-time polymorphism takes place when functions are invoked during run
time. It is also known as dynamic binding or late binding. Function overriding and virtual functions are used to
achieve run-time polymorphism.
class Addition
{
public:
void sum(int a, int b)
{
cout<<a+b;
}
void sum(int a, int b, int c)
{
cout<<a+b+c;
}
};
void main()
{
clrscr();
Addition obj;
obj.sum(10, 20);
cout<<endl;
obj.sum(10, 20, 30);
}
Output
30
60
It's a compile-time polymorphism because the compiler knows which function to execute before the program is
compiled. An overloaded function is called based on the number and type of parameters passed. Thus, the compiler picks
the correct function during compilation of the program.
public:
// Constructor to initialize count to 2
Count() : value(2) {}
int main() {
Count cnt1;
// Call the "void operator ++()" function
++cnt1;
cnt1.display();
return 0;
}
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 90
Output
Count: 4
It is also a compile-time polymorphism because the compiler knows which function to execute before the program is
compiled
int mian()
{
Base b; //Base class object
Derived d; //Derived class object
b.show(); //Early Binding Ocuurs
d.show();
getch();
}
Output
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 91
Base class
Derived Class
It's a runtime polymorphism because the function call is not resolved by the compiler, but it is resolved in the runtime
instead.
Virtual Function in C++
A virtual function is a member function of class that is declared within a base class and re-defined in
derived class. The classes that have virtual functions are called polymorphic classes.
When you want to use same function name in both the base and derived class, then the function in base class
is declared as virtual by using the virtual keyword and again re-defined this function in derived class without
using virtual keyword.
o It is used to tell the compiler to perform dynamic linkage or late binding on the function. The compiler
binds virtual function at runtime, hence called runtime polymorphism. Use of virtual function allows
the program to decide at runtime which function is to be called based on the type of the object pointed
by the pointer.
o 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.
o 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.
#include<iostream.h>
#include<conio.h>
class A
{
public:
virtual void show()
{
cout<<"Hello base class";
}
};
class B : public A
{
public:
void show()
{
cout<<"Hello derive class";
}
};
void main()
{
Clrscr();
A aobj;
B bobj;
A *bptr;
bptr=&aobj;
bptr->show(); // call base class function
bptr=&bobj;
bptr->show(); // call derive class function
getch();
}
Output
Hello base class
Hello derive class
Why do we need virtual functions?
Virtual functions are needed for many reasons, among them to eliminate ambiguity is one.
Example
#include <iostream>
using namespace std;
class A
{
int ;
public:
A() { x=5; } // constructor
void display()
{
cout << "Value of x is : " << x<<endl;
}
};
class B: public A
{
int y ;
public:
B() { y=10;}; //constructor
void display()
{
cout << "Value of y is : " <<y<< 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.
Let’s make it more clear with this example and know when and why we need to use virtual
functions.
#include <iostream>
using namespace std;
class Animal
{
public:
void my_features()
{
cout << "I am an animal.";
}
};
{
cout << "\nI am a reptile.";
}
};
int main()
{
Animal *obj1 = new Animal;
Mammal *obj2 = new Mammal;
Reptile *obj3 = new Reptile;
obj1->my_features();
obj2->my_features();
obj3->my_features();
return 0;
}
Output
I am an animal.
I am a mammal.
I am a reptile.
We created a three pointer objects *obj1, *obj2 and *obj3. And when we call
function my_features() using these pointer objects, the corresponding functions of the classes are
executed.
Now, what if we created an intermediate function to which we can pass objects of the corresponding class
to invoke functions. This should also do the same.
#include <iostream>
using namespace std;
class Animal
{
public:
void my_features()
{
cout << "I am an animal.";
}
};
}
};
//intermediate function
void intermediate_func(Animal *a1)
{
a1->my_features();
}
int main()
{
Animal *obj1 = new Animal;
Mammal *obj2 = new Mammal;
Reptile *obj3 = new Reptile;
intermediate_func(obj1);
intermediate_func(obj2);
intermediate_func(obj3);
return 0;
}
Output
I am an animal.
I am an animal.
I am an animal.
So let’s decode what went wrong and how it went that way. We created an intermediate function
intemediate_func which takes an object as the argument. And depending on the class pointed by that
object it invokes the member function of that class or at least we expected that way.
We supplied objects of Mammal and Reptile class to the intermediate function, which in return should
have invoked obj2->my_features() and obj3->my_features(). But that didn’t happen because of the
ambiguous situation aroused where compiler only invoked base class functions in all three situations.
So to overcome such ambiguity, virtual functions are used in base class. Virtual function dynamically
binds function call at runtime and allows it to be overridden by the member function of the derrived class.
Here is how a virtual function implemented in c++.
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void my_features()
{
cout << "I am an animal.";
}
};
};
class Reptile : public Animal
{
public:
void my_features()
{
cout << "\nI am a reptile.";
}
};
//intermediate function
void intermediate_func(Animal *a1)
{
a1->my_features();
}
int main()
{
Animal *obj1 = new Animal;
Mammal *obj2 = new Mammal;
Reptile *obj3 = new Reptile;
intermediate_func(obj1);
intermediate_func(obj2);
intermediate_func(obj3);
return 0;
}
Output
I am an animal.
I am a mammal.
I am a reptile.
Example of run-time polymorphism using two derived classes
#include <iostream>
using namespace std;
class Polygon {
public:
virtual void show() {
cout<<"Its a polygon"<<endl;
}
};
class Triangle : public Polygon {
public:
void show() {
cout<<"Triangle is 3 sided polygon"<<endl;
}
};
class Rectangle : public Polygon {
public:
void show() {
cout<<"Rectangle is 4 sided polygon"<<endl;
}
};
int main() {
Polygon *p;
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 97
Triangle t;
Rectangle r;
p = &t;
p->show();
p = &r;
p->show();
return 0;
}
Output
Triangle is 3 sided polygon
Rectangle is 4 sided polygon
Run-time Polymorphism with Data Members
Run-time polymorphism can also be achieved through data members.
Example of run-time polymorphism with data members
#include <iostream>
#include <string>
using namespace std;
class Animal {
public:
string name = "Animal";
};
class Dog : public Animal {
public:
string name = "Dog";
};
int main() {
Dog d;
cout<<d.name;
return 0;
}
Output
Dog
Why Polymorphism?
Polymorphism allows us to create consistent code. For example,
Suppose we need to calculate the area of a circle and a square. To do so, we can create a Shape class and derive two
classes Circle and Square from it.
In this case, it makes sense to create a function having the same name calculateArea() in both the derived classes rather
than creating functions with different names, thus making our code more consistent
As you can see, Manager and Clerk are both Employee. They are both Person too. What does it mean? It means that
Manager and Clerk classes inherit properties of Employee class, which inherits properties of Person class.
Both up-casting and down casting do not change the object by itself. When you use up-casting or down-casting you just
“label” an object in different ways.
UPCASTING
Upcasting is a process of creating a pointer or a reference of the derived class object as a base class pointer. You do not
need to upcast manually. You just need to assign derived class pointer (or reference) to base class pointer:
Upcasting is using the Super class's reference or pointer to refer to a Sub class's object. Or we can say that, the act of
converting a Sub class's reference or pointer into its Super class's reference or pointer is called Upcasting.
In other words, upcasting allows us to treat a derived type as though it were its base type. It is always allowed
for public inheritance, without an explicit type cast. This is a result of the is-a relationship between the base and derived
classes.
The derived class can inherit all the base class properties that include data members and the member function to execute
the function using the derived class object, as we do with a base object.
#include <iostream>
using namespace std;
class Base
{
public:
void disp()
{
cout << " It is the Super function of the Base class ";
}
};
};
int main ()
{
// create base class pointer
Base *ptr;
DOWNCASTING
Downcasting is an opposite process for upcasting. It converts base class pointer to derived class pointer. Downcasting
must be done manually. It means that you have to specify explicit typecast.
The reason for this restriction is that the is-a relationship is not, in most of the cases, symmetric. A derived class could add
new data members, and the class member functions that used these data members wouldn't apply to the base class.
Downcasting is not as safe as upcasting. You know that a derived class object can be always treated as a base class object.
However, the opposite is not right. For example, a Manager is always a Person; But a Person is not always a Manager. It
could be a Clerk too.
{
cout << " It is the function of the Parent class "<< endl;
}
};
int main ()
{
Parent pobj; // create Parent's object
Child *cobj; // create Child's object
return 0;
}
Output
It is the function of the Child class
Dynamic Casting
The dynamic_cast operator answers the question of whether we can safely assign the address of an object to a pointer
of a particular type.
Here is example to the previous one.
#include <string>
class Parent {
public:
void sleep() {
}
};
int main( )
{
Parent *pParent = new Parent;
Parent *pChild = new Child;
Type cast #1 is not safe because it assigns the address of a base-class object (Parent) to a derived class (Child) pointer.
So, the code would expect the base-class object to have derived class properties such as gotoSchool() method, and that is
false. Also, Child object, for example, has a member classes that a Parent object is lacking.
Type case #2, however, is safe because it assigns the address of a derived-class object to a base-class pointer. In other
words, public derivation promises that a Child object is also a Parent object.
dynamic_cast is an operator that converts safely one type to another type. In the case, the conversation is possible and safe,
it returns the address of the object that is converted. Otherwise, it returns nullptr.
If you want to use a dynamic cast for downcasting, the base class should be polymorphic – it must have at least one virtual
function. Modify base class Person by adding a virtual function:
The dynamic_cast is a runtime cast operator used to perform conversion of one type variable to another only on class
pointers and references. It means it checks the valid casting of the variables at the run time, and if the casting fails, it
returns a NULL value. Dynamic casting is based on RTTI (Runtime Type Identification) mechanism.
Dynamic casting checks consistency at runtime; hence, it is slower than static cast.
Take a look at the function signature of the dynamic cast below:
#include <iostream>
using namespace std;
class parent
{
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 102
}
};
class derived: public parent
{
};
int main ()
{
// create an object ptr
parent *ptr = new derived;
// use the dynamic cast to convert class data
derived* d = dynamic_cast <derived*> (ptr);
Why the base class’s constructor is called on creating an object of derived class?
To understand this you will have to recall your knowledge on inheritance. What happens when a class is inherited from
other? The data members and member functions of base class comes automatically in derived class based on the access
specifier but the definition of these members exists in base class only. So when we create an object of derived class, all of
the members of derived class must be initialized but the inherited members in derived class can only be initialized by the
base class’s constructor as the definition of these members exists in base class only. This is why the constructor of base
class is called first to initialize all the inherited members.
#include <iostream>
using namespace std;
// base class
class Parent
{
public:
// base class constructor
Parent()
{
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 103
#include <iostream>
using namespace std;
// first base class
class Parent1
{
public:
// first base class's Constructor
Parent1()
{
cout << "Inside first base class" << endl;
}
};
// second base class
class Parent2
{
public:
// second base class's Constructor
Parent2()
{
cout << "Inside second base class" << endl;
}
};
// child class inherits Parent1 and Parent2
class Child : public Parent1, public Parent2
{
public:
How to call the parameterized constructor of base class in derived class constructor?
To call the parameterized constructor of base class when derived class’s parameterized constructor is called, you have to
explicitly specify the base class’s parameterized constructor in derived class as shown in below program:
#include <iostream>
using namespace std;
// base class
class Parent {
int x;
public:
// base class's parameterized constructor
Parent(int i)
{
x = i;
cout << "Inside base class's parameterized "
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 105
"constructor"
<< endl;
}
};
// sub class
class Child : public Parent {
public:
// sub class's parameterized constructor
Child(int x): Parent(x)
{
cout << "Inside sub class's parameterized "
"constructor"
<< endl;
}
};
int main()
{
Important Points:
Whenever the derived class’s default constructor is called, the base class’s default constructor is called automatically.
To call the parameterized constructor of base class inside the parameterized constructor of sub class, we have to
mention it explicitly.
The parameterized constructor of base class cannot be called in default constructor of sub class, it should be called in
the parameterized constructor of sub class.
Syntax
Constructorname(datatype value1, datatype value2):datamember(value1),datamember(value2)
{
...
}
#include<iostream>
using namespace std;
class Base
{
private:
int value;
public:
// default constructor
Base(int v):value(v)
{
cout << "Value is " << value;
}
};
int main()
{
Base myobject(10);
return 0;
}
There are several cases where the use of an initializer list is absolutely necessary, these include:
An initialization list is used to initialize a data member of reference type. Reference types can only be initialized once
#include<iostream>
using namespace std;
class Base
{
private:
int &ref;
public:
Base(int &passed):ref(passed)
{
cout << "Value is " << ref;
}
};
int main()
{
int ref=10;
Base myobject(ref);
return 0;
}
const data members can be initialized only once, so they must be initialized in the initialization list.
#include<iostream>
using namespace std;
class Base
{
private:
const int var;
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 107
public:
Base(int constant_value):var(constant_value)
{
cout << "Value is " << var;
}
};
int main()
{
Base myobject(10);
}
If you have a field that has no default constructor (or a parent class with no default constructor), then you must specify
which constructor you wish to use.
#include<iostream>
using namespace std;
class Base_
{
public:
Base_(int x)
{
cout << "Base Class Constructor. Value is: " << x << endl;
}
};
int main()
{
InitilizerList_ mylist;
return 0;
}
There are two types of binding in C++: static (or early) binding and dynamic (or late) binding. the differences between
static and dynamic binding in C++.
1. the static binding happens at the compile-time, and dynamic binding happens at the runtime. Hence, they are also
called early and late binding, respectively.
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 108
2. In static binding, the function definition and the function call are linked during the compile-time, whereas in
dynamic binding, the function calls are not resolved until runtime. So, they are not bound until runtime.
3. Static binding happens when all information needed to call a function is available at the compile-time. Dynamic
binding happens when the compiler cannot determine all information needed for a function call at compile-time.
4. Static binding can be achieved using the normal function calls, function overloading, and operator overloading,
while dynamic binding can be achieved using the virtual functions.
5. Since all information needed to call a function is available before runtime, static binding results in faster
execution of a program. Unlike static binding, a function call is not resolved until runtime for later binding,
resulting in somewhat slower execution of code.
6. The major advantage of dynamic binding is that it is flexible since a single function can handle different types of
objects at runtime. This significantly reduces the size of the codebase and also makes the source code more
readable.
class ComputeSum
{
public:
int main()
{
ComputeSum obj;
cout << "Sum is " << obj.sum(10, 20) << endl;
cout << "Sum is " << obj.sum(10, 20, 30) << endl;
return 0;
}
Output:
Sum is 30
Sum is 60
Consider the following code, where we have a base class B, and a derived class D. Base class B has a virtual function f(),
which is overridden by a function in the derived class D, i.e., D::f() overrides B::f().
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 109
where the decision as to which class’s function will be invoked depends on the dynamic type of the object pointed to
by basePtr. This information can only be available at the runtime, and hence f() is subject to the dynamic binding.
using namespace std;
class B
{
public:
// Virtual function
virtual void f() {
cout << "The base class function is called.\n";
}
};
class D: public B
{
public:
void f() {
cout << "The derived class function is called.\n";
}
};
int main()
{
B base;
D derived;
B *basePtr = &base;
basePtr->f();
basePtr = &derived;
basePtr->f();
return 0;
}
Output:
In this context, dispatching just refers to the action of finding the right function to call. In the general case, when you
define a method inside a class, the compiler will remember its definition and execute it every time a call to that method is
encountered.
#include <iostream>
class A
{
public:
void foo();
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 110
};
void A::foo()
{
std::cout << "Hello this is foo" << std::endl;
}
Here, the compiler will create a routine for foo() and remember its address. This routine will be executed every time the
compiler finds a call to foo() on an instance of A. Keep in mind that only one routine exists per class method, and is shared
by all instances of the class. This process is known as static dispatch or early binding: the compiler knows which routine
to execute during compilation.
#include <iostream>
class B
{
public:
virtual void bar();
virtual void qux();
};
void B::bar()
{
std::cout << "This is B's implementation of bar" << std::endl;
}
void B::qux()
{
std::cout << "This is B's implementation of qux" << std::endl;
}
The thing about virtual functions is that they can be overriden by subclasses:
class C : public B
{
public:
void bar() override;
};
void C::bar()
{
std::cout << "This is C's implementation of bar" << std::endl;
}
Now consider the following call to bar():
B* b = new C();
b->bar();
If we use static dispatch as above, the call b->bar() would execute B::bar(), since (from the point of view of the compiler)
b points to an object of type B. This would be horribly wrong, off course, because b actually points to an object of
type C and C::bar() should be called instead.
Hopefully you can see the problem by now: given that virtual functions can be redefined in subclasses, calls via pointers
(or references) to a base type cannot be dispatched at compile time. The compiler has to find the right function definition
(i.e. the most specific one) at runtime. This process is called dynamic dispatch or late method binding.
For every class that contains virtual functions, the compiler constructs a virtual table, a.k.a vtable. The vtable contains an
entry for each virtual function accessible by the class and stores a pointer to its definition. Only the most specific function
definition callable by the class is stored in the vtable. Entries in the vtable can point to either functions declared in the
class itself (e.g. C::bar()), or virtual functions inherited from a base class (e.g. C::qux()).
In our example, the compiler will create the following virtual tables:
The vtable of class B has two entries, one for each of the two virtual functions declared in B’s scope: bar() and qux().
Additionally, the vtable of B points to the local definition of functions, since they are the most specific (and only)
from B’s point of view.
More interesting is C’s vtable. In this case, the entry for bar() points to own C’s implementation, given that it is more
specific than B::bar(). Since C doesn’t override qux(), its entry in the vtable points to B’s definition (the most specific
definition).
Note that vtables exist at the class level, meaning there exists a single vtable per class, and is shared by all instances.
Vpointers
but how exactly do they solve the problem? When the compiler sees b->bar() in the example above, it will
lookup B’s vtable for bar’s entry and follow the corresponding function pointer, right? We would still be
calling B::bar() and not C::bar()…
vpointers. Every time the compiler creates a vtable for a class, it adds an extra argument to it: a pointer to the
corresponding virtual table, called the vpointer.
Note that the vpointer is just another class member added by the compiler and increases the size of every object that has
a vtable by sizeof(vpointer).
Hopefully you have grasped how dynamic function dispatch can be implemented by using vtables: when a call to a virtual
function on an object is performed, the vpointer of the object is used to find the corresponding vtable of the class. Next,
the function name is used as index to the vtable to find the correct (most specific) routine to be executed. Done!
A pure virtual function (or abstract function) in C++ is a virtual function for which we don’t have implementation, we only
declare it. A pure virtual function start with virtual keyword and ends with = 0.
o A virtual function is not used for performing any task. It only serves as a placeholder.
o When the function has no definition, such function is known as "do-nothing" function.
o 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.
o 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.
o 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:
Let's take an example, Suppose, we have derived Car, Bike and Cycle classes from the Vehicle class, and we want to
calculate the Speed of all these Vehicles.
In this case, we can create a pure virtual function named calculateSpeed() in the Vehicle. Since it's a pure virtual function,
all derived classes Car, Bike and Cycle must include the calculateSpeed() function with implementation.
A pure virtual function doesn't have the function body and it must end with = 0. For example,
class Vehicle {
public:
// creating a pure virtual function
virtual void calculateSpeed() = 0;
};
Note: The = 0 syntax doesn't mean we are assigning 0 to the function. It's just the way we define pure virtual
functions.
#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:
class Helloworld
{
void show() {
cout<<"Good Morning"<<endl;
}
Here Helloworld class is containing a defined method and object can be created directly.
Crate an object
Helloworld obj;
obj.display();
Every concrete class have specific feature and these classes are used for specific requirement but not for common
requirement.
If we use concrete classes for fulfill common requirements than such application will get the following limitations.
Application will take more amount of memory space (main memory).
Application execution time is more.
Application performance is decreased.
To overcome above limitation you can use abstract class
We cannot create objects of an abstract class. However, we can derive classes from them, and use their data members and
member functions (except pure virtual functions).
2. Abstract class can have normal functions and variables along with a pure virtual function.
3. Abstract classes are mainly used for Upcasting, so that its derived classes can use its interface.
4. Classes inheriting an Abstract Class must implement all pure virtual functions, or else they will become Abstract
too.
// Derived class
class Derived : public Base {
public:
void show() {
cout << "Implementation of Abstract class and Pure virtual function.";
}
};
int main() {
Derived obj;
obj.show();
return 0;
}
Output
//#include <iostream>
using namespace std;
// Abstract class
class Shape {
protected:
float dimension;
public:
void getDimension() {
cin >> dimension;
}
// pure virtual Function
virtual float calculateArea() = 0;
};
// Derived class
class Square : public Shape {
public:
float calculateArea() {
return dimension * dimension;
}
};
// Derived class
class Circle : public Shape {
public:
float calculateArea() {
return 3.14 * dimension * dimension;
}
};
int main() {
Square square;
Circle circle;
return 0;
}
Output
Concrete class containing fully defined methods Abstract class have both undefined method
or implemented method. and defined method.
#include <string>
#include <iostream>
/*Declaring an abstract class*/
class AbstractClass{
public:
private:
std::string message;
};
class InterfaceClass{
public:
};
C++ template is also known as generic functions or classes which is a very powerful feature in C++. A keyword
“template” in c++ is used for the template’s syntax and angled bracket in a parameter (t), which defines the data type
variable. Just quickly review following point
It enables you to define a family of functions or classes that can operate on different types of information.
Templates are the better solution than C macros and void pointers and it is mostly useful when working with collections
and smart pointers.
Using template reduces multiple functions and decreases the code size. The task of writing the program will also be
simple for the programmer.
Function template
Class templates
Function template in c++ is a single function template that works with multiple data
types simultaneously, but a standard function works only with one set of data types.
Function templates are used to create a set of functions that apply the same algorithm
to different data types.
It works like a function with the only difference that it works on different data types at
once, but different functions are needed to perform identical task on different data
types.
Function overloading is used to perform identical operations on two or more types of
data, but function template is best approach to perform this task by writing less code
and the code is easier to maintain.
Syntax:
template < class Ttype> return_datatype function_name (argument_lists)
{
//Statements;
}
Above, Ttype is a placeholder name for the data type that is used by the function. The placeholder will automatically get
replaced with the actual data type by the compiler
#include<iostream>
using namespace std;
template <class A> A addition(A x, A y)
{
return ( x + y );
}
int main()
{
cout<<"Addition1 : "<<addition(4,4)<<endl;
cout<<"Addition2 : "<<addition(4.2,3.6)<<endl;
cout<<"Addition3 : "<<addition(4.0,4.7)<<endl;
return 0;
}
Output:
Addition1 : 8
Addition2 : 7.8
Addition3 : 8.7
In third cout statement, value 4.0 & 4.7 are passed instead of 4 & 4.7 and it will be integer and float which are different;
while the generic function claims both the parameters to be of same data type.
This is used when multiple functions do similar This is used when functions do identical
operations. operations.
Function overloading can take varying numbers Templates cannot take varying numbers
of arguments. of arguments.
Syntax:-
template<class T1, class T2,.....> return_type functionName (arguments of type T1, T2....) {
// body
}
The above syntax will accept any number of arguments of different types .
Output:-
Value->x: 15.2
Value->y: 1.3
Above, we used two generic types such as A and B in the template function
#include <iostream>
using namespace std;
template <class A>A addition(A num1, A num2)
{
return num1 + num2;
}
template <class A>A addition(float num1, A num2)
{
return num1 + num2;
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 122
}
template <class A>A addition(char num1, A num3)
{
return(num1 + num3);
}
int main ()
{
cout<<"Addition of Integer Numbers : "<<addition(10,20)<<endl;
cout<<"Addition of Float Numbers : "<<addition(10.0,15.5)<<endl;
cout<<"Addition of Characters : "<<addition('C',5)<<endl;
return 0;
}
Output:
Addition of Integer Numbers : 30
Addition of Float Numbers : 25.5
Addition of Characters : 72
The above program shows the addition of a special case char data type with integer type data.
template <class A>A addition(char num1, A num3)
{
return(num1+num3);
}
cout<<"Addition of Characters : "<<addition('C',5)<<endl;
In the above statement, in cout, addition function passes 'C' and 5 value, where it takes the ASCII value of C that is 67 and
assigns it to the num1 parameter and displays the addition.
Value->z: 2.1
class Name_of_the_class
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 123
... .. ...
... .. ...
};
From above, T is a placeholder for the data type used. It is also known as the template argument.
If you want to create a class template object then you will have to define the data type inside <> during creation.
Name_of_the_class<dataType> class_Object;
Example
Name_of_the_class<int> class_Object;
Name_of_the_class<float> class_Object;
Example
#include <iostream>
using namespace std;
template<class T>
class Add
{
public:
T n1 = 6;
T n2 = 1;
void addition()
{
std::cout << "n1+n2: " << n1+n2<<std::endl;
}
};
int main()
{
Add<int> data;
data.addition();
return 0;
}
Output:-
n1+n2: 7
Above, we created a template for class Add and within the main() function, we created the instance of class Add
class Name_of_the_class
// Body
The template can contain multiple arguments, and we can also use the non-type arguments In addition to the type T
argument, we can also use other types of arguments such as strings, function names, constant expression and built-in
types. Let' s see the following example:
In the above case, the nontype template argument is size and therefore, template supplies the size of the array as an
argument.
#include <iostream>
using namespace std;
template<class T, int size>
class A
{
public:
T arr[size];
void insert()
{
int i =1;
for (int j=0;j<size;j++)
{
arr[j] = i;
i++;
}
}
void display()
{
for(int i=0;i<size;i++)
{
std::cout << arr[i] << " ";
}
}
};
int main()
{
A<int,10> t1;
t1.insert();
t1.display();
return 0;
}
Output:
1 2 3 4 5 6 7 8 9 10
In the above example, the class template is created which contains the nontype template argument, i.e., size. It is specified
when the object of class 'A' is created.
Template Specialization
We use templates when we need functions/classes that apply the same algorithm to a several types. So we can use the
same function/class regardless of the types of the argument or result.
In other words, a template is a mechanism that allows a programmer to use types as parameters for a class or a function.
The compiler then generates a specific class or function when we later provide specific types as arguments. In a sense,
templates provide static (compile-time) polymorphism, as opposed to dynamic (run-time) polymorphism. A function/class
defined using template is called a generic function/class, and the ability to use and create generic functions/classes is one
of the key features of C++.
However, sometimes a template cannot (or should not) be applied to a certain types of data.
Function Specialization
In the example below, we have add() function which takes two parameter and returns the same type of data after adding
the two args.
#include <iostream>
using namespace std;
// specialized function
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 127
template<>
char add<char>(char x, char y)
{
cout << "Specialized template\n";
int i = x-'0';
int j = y-'0';
return i+j;
}
int main()
{
int a = 1, b = 2;
cout << add(a, b) << endl;
return 0;
}
When we designed the function add(T x, T y), the meaning was clear: add the two numbers. But when the user feeds
characters into the parameter, the meaning is not obvious. So, if the intention of the design is different from the initial one,
we may want to redefine the operation in a separate template. In other words, we do specialize the function.
Class Specialization
##include <iostream>
using namespace std;
template <>
class Test <int>
{
public:
Test()
{
// Initialization of data members
cout << "Specialized template \n";
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 128
}
};
int main()
{
Test<int> a;
Test<char> b;
Test<float> c;
return 0;
Output:
Specialized template
General template
General template
Less Redundancy:- Suppose, you want to find the area of a rectangle with parameters entered as integer and as well
as floating point values. Then without any difficulty, you can achieve this task with the help of templates. You don’t
have to create different functions for integer and floating point values.
Programming Effort Reduced:- You can use multiple blocks of code for multiple data types. It will reduce the
programming effort on the user’s end.
Reusability and Flexibility:- It provides the reusability of codes which you can use again. It also provides you code
flexibility.
Utility:- You can also combine templates with function overloading and multiple inheritance
Exception
An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the
program's Instructions.
One of the advantages of C++ over C is Exception Handling. Exception Handling in C++ is a process to handle
runtime errors. We perform exception handling so the normal flow of the application can be maintained even after
runtime errors.
In C++, exception is an event or object which is thrown at runtime. All exceptions are derived from std::exception
class. It is a runtime error which can be handled. If we don't handle the exception, it prints exception message and
terminates the program
1. try
2. catch
3. throw
try
{
// causes executions code
}
catch( ExceptionName e1 )
{
// catch block
}
catch( ExceptionName e2 )
{
// catch block
}
catch( ExceptionName eN )
{
// catch block
}
Try Block
It is one of the block in which we write the block of statements which causes executions at run time in other words try
block always contains problematic statements.
Catch block
It is one of the block in which we write the block of statements which will generates user friendly error messages in
other words catch block will suppose system error messages.
o out_of_range - Exception due to out of range i.e. size requirement exceeds allocation.
void main()
{
int a, ans;
a=10;
ans=a/0;
cout<<"Result: "<<ans;
}
Output
try
{
ans=a/0;
}
catch(int i)
{
cout<<"Denominator not be zero";
}
}
Output
Example
#include <iostream>
using namespace std;
int main()
{
int x = -1;
try {
cout << "Inside try \n";
if (x < 0) {
throw x;
cout << "After throw (Never executed) \n";
}
}
catch (int x) {
cout << "Exception Caught \n";
}
cout << "After Caught (Will be executed) \n";
return 0;
}
Output
Before try
Inside try
Exception Caught
After catch (Will be executed)
Example
There is a special catch block called ‘catch all’ catch(…) that can be used to catch all types of exceptions. For example, in
the following program, an int is thrown as an exception, but there is no catch block for int, so catch(…) block will be
executed.
#include <iostream>
using namespace std;
int main(){
try {
throw 10;
}
Default Exception
Example
Implicit type conversion doesn’t happen for primitive types. For example, in the following program ‘a’ is not implicitly
converted to int
#include <iostream>
using namespace std;
int main(){
try {
throw 'a';
}
catch (int x) {
cout << "Caught " << x;
}
catch (...) {
cout << "Default Exception \n";
}
return 0;
}
Output
Default Exception
Example
If an exception is thrown and not caught anywhere, the program terminates abnormally. For example, in the following
program, a char is thrown, but there is no catch block to catch a char.
#include <iostream>
using namespace std;
int main(){
try {
throw 'a';
}
catch (int x) {
cout << "Caught " << x;
}
return 0;
}
Output
Example
Unlike Java, in C++, all exceptions are unchecked. Compiler doesn’t check whether an exception is caught or not (See
this for details). For example, in C++, it is not necessary to specify all uncaught exceptions in a function declaration.
Although it’s a recommended practice to do so. For example, the following program compiles fine, but ideally signature
of fun() should list unchecked exceptions.
#include <iostream>
using namespace std;
// Here we specify the exceptions that this function throws.
void fun(int *ptr, int x) throw(int *, int){
if (ptr == NULL) {
throw ptr;
}
if (x == 0) {
throw x;
// some functionality.
}
}
int main(){
try {
fun(NULL, 0);
}
catch (...) {
cout << "Caught exception from fun()";
}
return 0;
}
Output
Example
#include <iostream>
using namespace std;
Class Exam{
public:
Exam() {cout << "Constructor of Exam \n"; }
~ Exam() {cout << "Destructor of Exam \n"; }
[email protected] contact 03354900611
OOPS IN C++ Notes P a g e | 134
};
int main(){
try {
Exam e1;
throw 10;
}
catch (int i) {
cout << "Caught " << i << endl;
}
return 0;
}
Output
Constructor of Test
Destructor of Test
Caught 10
Example
When an exception is thrown, all objects created inside the enclosing try block are destructed before the control is
transferred to catch block.
#include <iostream>
using namespace std;
int main(){
try {
try {
throw 20;
}
catch (int n) {
cout << "Handle Partially ";
throw; // Re-throwing an exception
}
}
catch (int n) {
cout << "Handle remaining ";
}
return 0;
}
Output
User-Defined Exceptions
The C++ std::exception class allows us to define objects that can be thrown as exceptions. This class has been defined in
the header. The class provides us with a virtual member function named what.
This function returns a null-terminated character sequence of type char *. We can overwrite it in derived classes to have
an exception description.
Example
#include <iostream>
#include <exception>
using namespace std;
try {
throw newex;
}
catch (exception ex) {
cout << ex.what() << endl;
}
return 0;
}
Output
Excep Occurred
1. You will separate your error handling code from your normal code. The code will be more readable and easier to
maintain.
2. Functions can handle the exceptions they choose. Even if a function throws many exceptions, it will only handle some.
The caller will handle the uncaught exceptions.
3. Grouping of Error Types: In C++, both basic types and objects can be thrown as exception. We can create a hierarchy of
exception objects, group exceptions in namespaces or classes, categorize them according to types.