0% found this document useful (0 votes)
35 views126 pages

C Material

This document provides an introduction to object-oriented programming with C++. It begins with an overview of procedural programming limitations in C like global data insecurity and lack of data hiding. Object-oriented programming aims to overcome these issues by combining data and functions that operate on that data into single units called objects. The key concepts of object-oriented programming like abstraction, encapsulation, classes, inheritance and polymorphism are then discussed in further chapters. The document also provides an index of topics that will be covered, including constructors, destructors, operator overloading, exceptions and multithreading.

Uploaded by

Naresh Kumar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
35 views126 pages

C Material

This document provides an introduction to object-oriented programming with C++. It begins with an overview of procedural programming limitations in C like global data insecurity and lack of data hiding. Object-oriented programming aims to overcome these issues by combining data and functions that operate on that data into single units called objects. The key concepts of object-oriented programming like abstraction, encapsulation, classes, inheritance and polymorphism are then discussed in further chapters. The document also provides an index of topics that will be covered, including constructors, destructors, operator overloading, exceptions and multithreading.

Uploaded by

Naresh Kumar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 126

C++ www.cranesvarsity.

com

OOP with C++

It is advised that the participants revise the C Basics and Data


Prerequisite
structure module that they have already done in Cranes.

© Cranes Varsity V2.1-2020-21 1/ 126


C++ www.cranesvarsity.com

INDEX

Module Oops with C++


Session Page Lab
Sl Topics / Subtopics
No: No:
1 Introduction to object oriented programming 1 4 Y
Comparison between procedural & object derivative languages 4
Understanding OOP concepts 4
Basic input / output program including 8
o cin, cout, endl 8
o >> and << 8
o Bool data type 8
o Understanding namespace 9
2 PROCEDURAL APPROACH IN c++ 2 12 Y
Default Arguments 12
Inline functions 13
Reference variable and Call by reference 15
Function overloading and Name mangling 16
3 Object oriented approach in c++ 3 19 Y
Abstraction 19
Encapsulation 19
o Difference between C and C++ Structure 19
o Access Specifiers – Private and Protected 20
o Difference between C++ structures and C++ classes 20
This pointer 24
o Applications 25
o Concatenation of functions calls 25
o Ambiguity resolving 25
4 Constructors and Destructors 4 27 Y
Default constructors 29
Parameterized constructors 29
Dynamic memory allocation techniques in C++ 32
Destructors 32
5 Copy constructors 5 36 Y
Shallow copy 39
Deep copy 39
Static and constants 45 Y
Static data members and static function members 46
Constant data members and constant function members 49
6 Friends and operator overloading 7 52 Y
Friends 53
Operator overloading 53
o Binary operator 54
o Operator function as a member function 54

© Cranes Varsity V2.1-2020-21 2/ 126


C++ www.cranesvarsity.com

o Operator function as a friend function 55


Unary operator 58
o Operator function as a member function 59
o Operator function as a friend function 59
Overloading of insertion and extraction operator 60
Overloading of assignment operator 62
Operators which cannot be overloaded 68
7 Generic programming 9 69 Y
STL 70
o Containers 70
o Algorithms 72
o Iterators 72
8 Generalization 11 77 Y
Inheritance and “is a ” relationship 78
Difference between private and protected members 78
Types of inheritance 79
o Death of diamond and virtual base class concept. 87
o Constructor and destructor call 92
9 Run time polymorphism 13 98 Y
Function overriding 100
Virtual functions 104
Virtual destructor concept 107
Abstract classes 111
10 Exception handling 14 114 Y
11 Multithreading 15 122 Y
Creating Threads 122
Terminating Threads 123
Passing Arguments to Threads 124
Joining and Detaching Threads 125

© Cranes Varsity V2.1-2020-21 3/ 126


C++ www.cranesvarsity.com

CHAPTER - 1
INTRODUCTION TO OBJECT ORIENTED PROGRAMMING

There are different programming paradigms (model of programming) like Imperative, Functional, and
Object-Oriented.
 Imperative Paradigm: that describes computation in terms of statements that change a
program state ; a program is a sequence of instructions.
o Ex: FORTRAN, Algol, Pascal, C
 Functional Paradigm: The natural abstraction here is the function; a program is a collection of
functions.
o Ex: C
 Object-Oriented: programs are made up of collection of individual units called objects that
have a distinct purpose.
o Ex: Simula67, Smalltalk, C++, Java, Eiffel, Java, C#

We are already aware of the structured programming language. Before we get into the Object oriented
programming language, let us list out the advantages and limitations of structured programming
languages.

A structured programming language, the distinguishing feature of a structured language is


compartmentalization of code and data. This is the ability of a language to section off and hide from
the rest of the program all information and instructions necessary to perform a specific task.
Specifically the C programming language offers the following features
 Modularity
 Easy debugging
 Easy to understand
 Portability
 Document-ability
 Maintainability
 “Top-down” approach to programming

These programming advantages make working with a structured programming language like C a very
popular choice. However, there are certain inherent difficulties associated with structured programming
in general and C in particular. Some of the most visible of these are given below

 Global data insecurity


 Less data hiding.
 Functions are not written specific to the data
 Difficulties in coding large projects.

These “limitations” make developing large applications very difficult in C. To overcome these specific
issues i.e. data hiding and functions not being related to data, the concept of objects were developed.

Object Model languages

In object Model languages, data was considered as a critical element in program development. In
these types of languages, data and the code acting upon the data is combined into a single unit
termed as objects. Or in other words, object is a self-contained entity that consists of both data and
operations to manipulate that data. An entity can be considered as an object only if it has

 An identity
 A state
 A behavior

© Cranes Varsity V2.1-2020-21 4/ 126


C++ www.cranesvarsity.com

The identity is the feature of an object, which distinguishes the object uniquely.
Objects are identified and distinguished from one another through their identities.
At a given point of time during execution, two objects may have the same state and the same
behavior, but they are distinguishable through their identities.

The state of an object is simply gives the current status of an object.


Each object has its own set of local variables; the values of these variables represent the current state
of the object
The behavior of an object is simply an action that an object performs in order to change the state or as
a result of the state change itself. Member functions define the behavior.

Object World

There are two types of object Model languages

 Object Oriented Programming language


 Object Based Programming language

Object Based Programming language

Object based programming languages can be further categorized into two types:
Languages that have only Encapsulation and Polymorphism

E.g.: Visual Basic

Languages that do not use the language key-word “class”

E.g.: Java Script

The concept of Object Based programming language is just of academic interest to us. At this point of
time, we are more interested in the concept of object oriented programming.

Object Oriented Programming Approach

The ultimate aim of an object oriented approach is to design / model a real world entity.
An application program in OOP’s will be a collection of interacting objects. We have already seen that
object is a real world entity. So when it comes to designing of real world complex systems, first step is
to trace out all the real world entities in that system which can be considered as objects.

For e.g.: consider a college management system. It can be broken down into the following real world
entities.

College management system

Colleges Students Trainer

Courses Departmen
ts
Computer Projector

© Cranes Varsity V2.1-2020-21 5/ 126


C++ www.cranesvarsity.com

Each and every real world entity will be surrounded by large number of details. So the next step is to
trace out those details which are very relevant to the current application. This process is known as
abstraction.

Abstraction

For e.g.: let’s consider the real world entity student. As a person in the real world, he will be
surrounded by so many details as shown below.

Fees paid

College id Marks obtained

Name & Identity Course selected

Family details Genetic Details

Medical Details

After abstraction for a student

College id Marks obtained

Name & Identity Course selected

Fees paid

From the above example we can understand that abstraction gives programmers, an ability to cope
with the complexity issues without getting overwhelmed by the details.

Summarizing:

Abstraction allows the user to remove the extras and concentrate only on the essential
details of an object that distinguish it from all other kinds of objects.
.

Once the real world entities are traced out, the next step is to technically implement each and every
real world entities. This technical implementation of abstraction is known as encapsulation.

Encapsulation

Encapsulation is combining both data and functions together in a single unit.


Encapsulation is a technical implementation of Abstraction is achieved through the separation;
Separation refers to Separating interface and implementation; that involves defining a component by
specifying a public interface, known to the clients, separate from the details of how the component is
realized.

© Cranes Varsity V2.1-2020-21 6/ 126


C++ www.cranesvarsity.com

As far as an object oriented programmer is concerned, his entire world revolves around objects. And
object is nothing but a collection of data and functions acting upon the data. In this context, interface
refers to function names and implementation refers to data and the definition of the functions. So by
separating the interface and implementation, object oriented programmer achieves one of the major
goals, i.e. data hiding or information hiding.

Encapsulation refers to the technical implementation of abstraction and is achieved


through separation.
Composition:

Using abstraction and encapsulation, the entities traced out in the real world system is technically
implemented. All these entities in the real world are related to each other. For e.g.: in the college
management system, a student is related to college at the same time student is even related to
course. In the same manner course is related to department at the same time course is related to
instructor also.

INTRODUCTION TO C++

So what is C++? Originally known as C with classes, C++ is a general-purpose, platform-neutral


programming language that supports object-oriented programming and other useful programming
paradigm/s, including structured programming, generic programming and imperative programming. It
was invented in AT&T bell labs in 1979 by Bjarne Stroustrup.

C++ is a superset of C, and offers backward compatibility with the language. This is important because
it offers the user the advantage of

 Reusing C code in new C++ programs


 Efficiency
 Platform neutrality
 Relatively quick migration from C to C++

Because of its rich ‘C’ legacy, it has retained some of its drawbacks including manual memory
management, unchecked array bounds and pointers.

So is C++ better than C? The primary reason for a C programmer to switch to C++ is its Object
oriented programming style.

C programmers who do not prefer to switch to object-oriented programming still benefit from a
migration to C++ because of its tighter type-safety, strongly typed pointers, improved memory
management, and many other features that make a programmer's life easier.

Some of these improvements are


 Better memory management. In C, library functions are called to dynamically allocate and
release memory. C++ makes use of two operators new and delete to allocate and de-
allocate objects dynamically.
 C++ treats built in data types and user defined data types in exactly the same manner. For
example, a structure or a union name can be used directly in declarations of a structure
variable. It is not required that the struct or union keywords appear in the declarations of
these variables. Apart from this, addition, multiplication, assignment etc. operators can be
used directly on a structure variable. Thus a user defined data type can also be used just
as a built-in type.
 C++ provides a new type of argument passing mechanism known as call by reference. C++
also supports pass by value and pass by address which is same as in C.
 Local variable can be declared just before their use in the code.

© Cranes Varsity V2.1-2020-21 7/ 126


C++ www.cranesvarsity.com

 C++ is a stricter language as compared with C. It is strict in returning from functions,


mandatory prototyping of functions and few new typecasting operators that enforce type-
checking.

The bool data type

The up-gradations in C++ also included a new data type – the bool type. A bool variable can be used
like any other data type. It uses one byte of memory and can have only two values defined by two new
C++ keywords: true and false. The values of these keywords are
 true = 1
 false = 0

The bool data type is very useful in conditional expressions.

C++ Standards

Unlike many other programming languages, C++ does not have versions. Rather, it has an
International ANSI/ISO Standard, ratified in 1998, that defines the core language, its standard libraries,
and implementation requirements. The C++ Standard is treated as a skeleton on which vendors might
add their own platform-specific extensions, mostly by means of code libraries. However, it's possible to
develop large-scale applications using pure standard C++, thereby ensuring code portability and
easier maintenance.

However unlike C, C++ is an unfinished language. This means that a lot of additions are continually
made to the language, thus making the language better and easier for programming. Since compiler
designed always lag behind due to the additions of new features in the language, C++ has become a
highly compiler dependent programming language. Current C++ standard is ISO/IEC 9899:1999 also
known as the C99 standards.
Lets first try to concentrate on the procedural side of programming in C++ and let’s see what are the
additional features added along with C++ which makes even the procedural approach of C++ better
than C.

Let’s look at our 1st C++ program


// First C++ program – Calculation of distance Output :
#include<iostream.h> Enter speed and time
int main(void) 12
{
10
int Speed, Time;
Calculated distance is =

cout << “Enter speed and time” << endl; 120


cin >> Speed >> Time;
int Distance;
Distance = Speed * Time;
cout << “Calculated distance is = ” << Distance << endl;
return 0;
}

Clearly, one can see a lot of new terms in this program. Let us take them up one by one. Let’s start
from the first line.

© Cranes Varsity V2.1-2020-21 8/ 126


C++ www.cranesvarsity.com

As you might have guessed, the first line indicates comments. Since C++ have backward compatibility
with C language, it is perfectly legal to use C specific comment syntax, ie) /* * /. But sticking on to C++
programming syntax, // is used for single line comments and / * */ is used for multi-line comments.
Second line is the pre-processor directive which instructs the pre-processor to include the contents of
the header file mentioned in angular braces. “iostream.h” is a header file which has the functionality
similar to “stdio.h”. ie) it is going to help in input /output functionalities. Just like C, in C++ also, the
starting point of any program is main.

The next line indicates variable declaration. Only difference here is, in C++ there is flexible variable
declaration which is indicated by the declaration of distance just before its usage.

Statement starting with cout and cin behaves like printf and scanf functions in C. To understand the
working of these statements, a thorough knowledge of generalization and operator overlading is
needed. For the time-being, lets briefly see how it is working.

“cout” and “cin” are pre-defined objects of standard output and input stream respectively. First let’s
start with the term stream. Stream just refers to flow of data. Data can flow either towards the monitor
ie) standard output device or data can flow from the keyboard ie) standard input device. If the data is
flowing towards the monitor,stream associated is output stream and if data is flowing from the
keyboard, stream associated is input stream.

“cout” is an object associated with output stream. Ie) cout helps the program to communicate with the
monitor. In the similar manner, cin is an object associated with input stream. Ie) cin helps the program
to interact with the keyboard.

If you notice, in the cout and cin statements, in between the data and the predefined objects, two
operators << and >> are used. These operators were used as shift operators in C language. But in this
context << is termed as put to / insertion operator and >> is termed as get from / extraction operator.

Put to operator puts / inserts the contents on its right to the object cout which will push the contents
directly to the monitor.

Monitor variable
(standard output) cout <<

Get from operator gets / extracts the contents of the object cin and puts it into the variable on the right.

Keyboard
(Standard input) cin >> variable

The only other thing unfamiliar in this program is endl. “endl” comes under a classification called
manipulators. Manipulators are functions specifically designed to be used in conjunction with the
insertion (<<) and extraction (>>) operators on stream objects.
Manipulator endl works exactly like the newline character, ‘\n’ in the C language.
Try to compile this program and notice the warning arising. The warning in this program is due to
the .h extension in the preprocessor directive. In C++, .h extensions are not used along with header
file. Instead another new concept known as namespaces is introduced. First let’s see what namespace
are. Then we will see how it is related to header files.

Namespaces
A namespace is a mechanism for expressing logical grouping. That is, if some Names logically belong
together according to some criteria, they can be put in a common namespace to express that

© Cranes Varsity V2.1-2020-21 9/ 126


C++ www.cranesvarsity.com

fact.Names in C++ can refer to variable names, function names, structure names or enumerator
names.
When a program uses different global names written by different programmers, there is a possibility
that two programmers may use the same name for two different things. Duplication of these names will
result in name-clashes. A way to deal with this problem is namespaces.

C++ adds the ability to create named declarative regions in the global section one whose main
purpose is to provide an area in which we can declare logically related names. These named
declarative regions are termed as namespaces. The names in one name space don’t conflict with the
same names declared in other namespaces and there are mechanisms for letting other parts of a
program use items declared in a namespace.

Namespaces can be located at the global level or inside other namespaces but they cannot be placed
in a block. Thus the names declared in a namespace will have external linkage. The syntax for
namespace is :
namespace namespace_name
{
//Declared names
}

Lets take an example :


namespace ns1
{
int a=10;

}
namespace ns2
{
float a=10.1;

The names in the namespace can be accessed outside by using either of the following two things.
 Scope resolution operator ( : : )
 Using keyword

Scope resolution Operator:

Scope resolution operator helps to qualify a name in a namespace .

The syntax is: namespace_name:: name;

writing the main for the above namespace using scope resolution operator :

int main()
{
Cout<<ns1::a<<endl;
Cout<<ns2::a<<endl;
}

© Cranes Varsity V2.1-2020-21 10/ 126


C++ www.cranesvarsity.com

Using Keyword

Having to qualify names every time they are used is not a good programming practice. So C++
provides two mechanisms, “using declaration” and “using directives” to simplify using namespace
names. The using declaration lets you make particular names available while using directives makes
the entire names in namespace available.

The syntax for using declaration is :

using namespace_name :: name;

Lets modify the above main using namespace declaration:

int main()
{
Cout<<using ns1 :: a<<endl;
Cout<<using ns2::a<<endl;
}

The syntax for using directive is

using namespace namespace_name;

Lets modify the above main using namespace directive:

int main()
{
using namespace ns1 ;
cout<<a<<endl;
}

Note : Find the difference between namespace declaration and namespace directive.

In C++ all the standard names like cout, cin are put in a single namespace unit called as standard
namespace.We use it along with our header file iostream. All existing c libraries are prefixed with letter
c and used in c++.For e.g stdio.h will be used as cstdio, math.h will be used as cmath.

© Cranes Varsity V2.1-2020-21 11/ 126


C++ www.cranesvarsity.com

CHAPTER -2

PROCEDURAL APPROACH IN C++

Default arguments

Consider the following program.

//Program to calculate the wages of employees


#include <iostream>
using namespace std;

float Calculate_Wage(float, float);

int main(void)
{
float Hours, Money;

cout << “Enter the number of hours and money per hour” << endl;
cin >> Hours >> Money;
cout << “ wage of the person who worked for ” << Hours << “hours is = ”;
cout << Calculate_Wage(Hours, Money) << endl;
return 0;
}
float Calculate_Wage(float HoursWorked, float MoneyPerHour)
In{ C language, if a function is defined with N arguments, we have to invoke that function by
return (HoursWorked * MoneyPerHour);
}
In a C language,if a function is defined with N arguments,we have to invoke that function by
passing the exact number of argument. For e.g. : in the above function Calculate_Wage(float, float)
which is defined with two arguments, we have to invoke this function by passing two arguments only.

But in C++, using the concept of default arguments, we can invoke a function which is taking N
arguments either by passing N number of arguments or by passing a lesser number of arguments.
The supplying of these arguments is not done randomly. There are certain rules that have to be
followed for the same.

They are

 When a function is called with missing arguments, it is always assumed that the trailing
arguments are missing. So the default arguments should be supplied by the programmer
from trailing edge to leading edge. i.e. RHS to LHS.

 The default arguments should be provided in the function prototype.

 The default value given for an argument can be a global constant, a global variable, or a
function call (return value).

Modifying the above program

© Cranes Varsity V2.1-2020-21 12/ 126


C++ www.cranesvarsity.com

//Program to calculate the wages of employees after providing default values

#include <iostream>
using namespace std;
float Calculate_Wage(float, float = 50);

int main(void)
{
float Hours, Money;

cout << “Enter the number of hours and money per hour ” << endl;
cin >> Hours>>Money;
cout << “ wage of the person who worked for ” << Hours << “hours is = ”;
cout << Calculate_Wage(Hours, Money) << endl;
cout << “Enter the number of hours ” << endl;
cin >> Hours;
cout << “ wage of the person who worked for ” << Hours << “hours is = ”;
cout << Calculate_Wage(Hours) << endl;
return 0;
}

float Calculate_Wage(float HoursWorked, float MoneyPerHour)


{
return (HoursWorked * MoneyPerHour);
}

OUTPUT

Enter the number of hours and money per hour


2
30

Wage of the person who worked for 2 hours is


60

Enter the number of hours

When the function was called with both arguments, the user supplied arguments were used by the
function. In the second case, when only one argument was supplied, the function uses the user
supplied value for the 1st argument and the default value for the 2nd argument.

Inline functions

From C language, we have seen the advantages of using functions in our programs. But functions do
have its own disadvantages and the most important among them is function call overheads. To avoid
the disadvantages of functions, macros were introduced.

© Cranes Varsity V2.1-2020-21 13/ 126


C++ www.cranesvarsity.com

Macros are a preprocessor directive which performs in-place code expansion. Macros are very
powerful programming tools that are advantageous for the following reasons

 They help avoid function call overheads

 Small changes in code can be done more easily

However, macros are not without disadvantages.

 They increase code size

 They are directives, meaning that they cannot be ignored

 There is no type checking in macros.

In C++, an attempt was made to remove these disadvantages and retain the advantages. Thus inline
functions were created. The advantages of inline functions are

 It is also in-place code substitution like macro; so that advantage is retained


 It is compiler dependent. i.e. whether or not an inline request is granted is dependent on
the compiler
 Since these are compiler dependent, there is a strict type checking carried out

Syntax:
inline return_type function_name (argument declaration)
{
function body
}

The difference between inline functions and macros are

Macro Inline Functions


Pre-processing time Compile-time
Directive, must be substituted Request, can be ignored
No type checking Type checking done

//Program to illustrate the use of inline functions


#include <iostream>
using namespace std;

inline int square(int x)


{
return (x * x);
}

int main(void)
{
int x=5;
cout << square(x+2) << endl;
return 0;
}

OUTPUT
49
© Cranes Varsity V2.1-2020-21 14/ 126
C++ www.cranesvarsity.com

All the functions with the keyword inline attached to it won’t be considered as inline functions. Some
situations where inline request will be ignored by some compliers are

 When the function has many lines of code


 When the function has control structures and loops and switches
 When the function has static variables
 When the function is making recursive calls
 If the functions are not defined in the same translation unit
 If inline function function is called through a function pointer.

Reference Variables and Call by reference

A reference is an alias or alternative name for variable. References are internally treated as constant
pointers implying that references, like all constants have to be initialized at the point of declaration.

Syntax:

data_type& reference_variable = variable_of_same_data_type

Note that the “&” operator appears on the left hand side and not on the right hand side of the
assignment operator. After declaration and initialization is done, a reference variable is used exactly
like a normal variable would be.

Example:

int var = 3; // declaration and initialization of a variable

int& ref_var = var; //declaration and initialization of reference variable

int *p = &var //declaration and initialization of pointers

Here ref_var is a reference to a integer variable and is initialized to integer variable var.

The memory picture will look something like this.

P & var

3 var

ref_var &var

Any changes to the reference (ref_var) variable will be reflected to the referred variable (var) also. In
C++ using references is preferred over using pointers because

 References have a simpler syntax

 Fewer chances of memory corruption since they are internally treated like constant pointers

© Cranes Varsity V2.1-2020-21 15/ 126


C++ www.cranesvarsity.com

C++ defines passing to functions using two methods

Pass by value:
The pass by value is same as in C i.e. a copy of the variable is passed to the called function. Any
change made to the copy of the variable in the called function is not reflected in the calling function

Call by reference:
C++ references are used for implementing call by references. This method is simpler compared to
using pass by address using pointers.

Let’s see how it is done using a program:

//Program to illustrate call by reference


#include <iostream>

using namespace std;

void Get_Values(int &, int &);


void Swap_Values(int &, int &);
void Show_Values(int &, int &);

int main()
{
int Num1, Num2;

Get_Values(Num1, Num2);
cout << “Values before swapping” << Num1 << ‘\t’ << Num2 << endl;
Swap_Values(Num1, Num2);
Show_Values(Num1, Num2);
}
void Get_Values(int &Val1, int &Val2)
{
cout << “Enter two values” << endl;
cin >> Val1 >> Val2;
}

void Swap_Values(int &Val1, int &Val2)


{
int Temp;

Temp = Val1;
Val1 = Val2;
Val2 = Temp;
}
void Show_Values(int &Val1, int &Val2)
{
cout << “Values after swapping” << Val1 << ‘\t’ << Val2 << endl;
}

OUTPUT

Enter two values


10
20

© Values before
swapping 10
Cranes Varsity 20 V2.1-2020-21 16/ 126
Values after swapping 20 10
C++ www.cranesvarsity.com

Function Overloading

Function overloading provides the flexibility of grouping together functions performing similar jobs
under a single name. The compiler binds the function call to the function definition based on the
function name and argument list. The arguments of the overloaded function should differ in terms of
 Type of arguments
 Order of arguments
 Number of arguments
 Constantness of Pointer and References
 Volatileness of Pointer and References

Note:
One important point here is that the return type of a function in no manner helps in performing function
overloading

//Program to illustrate function overloading


#include <iostream>
using namespace std;

#define PI 3.14
void Area(int);
void Area(float);
void Area(int, int);
int main()
{
int Side = 10;
float Rad = 12.2f;
int Length = 15, Breadth = 4;

Area(Side);
Area(Rad);
Area(Length, Breadth);
}

void Area(int Side_Square) //Area of a Square


{
cout << “Area of a square with side = ” << Side_Square << “is ” << ‘\t’;
cout << Side_Square * Side_Square << endl;
}
void Area(float Radius) //Area of a circle
{
cout << “Area of a circle with radius = ” << Radius << “is ” << ‘\t’;
cout << PI * Radius * Radius << endl;
}
void Area(int Len_Rect, int Brdth_Rect) //Area of a rectangle
{
cout << “Area of a rectangle with length = ” << Len_Rect << “and”;
cout << “breadth = ” << Brdth_Rect << “is ” << ‘\t’;
cout << Len_Rect * Brdth_Rect << endl;
© Cranes Varsity V2.1-2020-21 17/ 126
}
C++ www.cranesvarsity.com

OUTPUT

Area of square with side = 10 is 100


Area of a circle with radius = 12.2 is 467.35
Area of a rectangle with length = 15 and breadth = 4 is 60

Note:

How does C++ keep track of which overloaded function is which? This is achieved using “Name
mangling” or “Name decoration”. During Name Mangling, C++ compiler internally converts the function
name into a distinct name based on function name and the formal parameter types specified in the
function prototype.

**********

© Cranes Varsity V2.1-2020-21 18/ 126


C++ www.cranesvarsity.com

CHAPTER - 3
OBJECT ORIENTED APPROACH IN C++

We have already seen what abstraction is with an example. Now let’s see how encapsulation is done.
Encapsulation is achieved using classes and objects in C++.

Introduction to classes

A class is a single unit that is a representation of the abstracted information. It is a model, like the blue
print of a house. The blue print of the house gives all the information for building a house. Using the
same blue print, more than one house can be constructed. Same way the class is just an information
provider from which objects are created. The same class can be reused to create many objects. The
process of creation of objects is called instantiation.

A class in C++ looks very similar to the structures used in C. The difference is in the use. Before we
get into the differences between a class and structure, let us look at the differences between a
structure in C and C++. A structure in C is just a collection of similar and / or dissimilar data types. A
structure in C++ contains data and the functions that work on the data. Moreover in C++, in-order to
achieve data security, access specifiers are used along with structures.

Syntax of a C++ structure:

struct structure_name
{
Access specifiers:
Data members & Function Members
};

Access specifiers can be public, private or protected.


 public: data and functions defined under this access specifier can be accessed outside the
class using a structure variable.
 private: data and functions defined under this access specifier can be accessed only within
the class member functions
 protected: data and functions defined under this access specifier will behave in the same
manner as those under the private access specification. The difference between these two
access specifiers will be highlighted while discussing generalization.

Few more differences between C and C++ structures are shown below in the table.

Structures in C Structures in C++


Contains only data members Contain both data and function
members
While creating structure variable Need not use the struct keyword while
struct keyword is needed creating the variable
No concept of empty structure Size-of empty structure is 1 byte in
gcc compiler (to be discussed )
Cannot have static data Can have static data members
No concept of access specifiers Access specifiers can be used for
providing data security

© Cranes Varsity V2.1-2020-21 19/ 126


C++ www.cranesvarsity.com

The difference between a C++ structure and a C++ class is by default the data members and function
members of a structure are public where as in classes it is private.
Syntax of a C++ class:

Keyword
Keyword

class class_name User given name


{
private: Access
private data and functions specifier
public: Access
public data and functions specifier
};
With these basics of a class in place, let us look at some of the general programming practice followed
throughout the object oriented world.
 The class name should convey the meaning of the abstraction that the programmer is creating
 Data is normally hidden, i.e. either private or protected
 Functions are normally visible, i.e. public

Using this basic concept of classes, lets try to do the encapsulation of the previously done abstraction
Student. We have already traced out the attributes of student. Now let’s try to include few behaviors
and let’s put it in a diagrammatic representation.

Student Class Name


- Name : string
- College_Id : integer
- Course : string Private Members
- Total_Marks : float (Indicated by ‘-’)
- Fees : float
+ Enroll_Student():void
+ Set_Marks():void
+ Public Members
Calculate_Percentage():float (Indicated by ‘+’)

//Program to show encapsulation of Student


#include <iostream>
using namespace std;
class Student
{
private:
char Name[20];
int College_Id;
char Course[20];
float Total_Marks;
float Fees;
public:
void Enroll_Student(char *n, int i, char *c, float f)
{
strcpy(Name, n);
College_Id = i;
strcpy(Course, c);

© Cranes Varsity V2.1-2020-21 20/ 126


C++ www.cranesvarsity.com

Fees = f;
Total_Marks = 0;
}
void Set_Marks(float total)
{
Total_Marks = total;
}
float Calculate_Percentage(int no_subjects)
{
return (Total_Marks / no_subjects);
}
};

int main()
{
char name[20];
int id;
char course[20];
float fees;
Student Anu;

cout << "Enter the name, id, course selected and fees" << endl;
cin >> name >> id >> course >> fees;

Anu.Enroll_Student(name, id, course, fees);

float marks;
int no_subjects;

cout << "Enter total marks and total no of subjects" << endl;
cin >> marks >> no_subjects;

Anu.Set_Marks(marks);
cout << Anu.Calculate_Percentage(no_subjects) << endl;

return 0;
}

The member functions defined in the class are by default considered as inline functions. The functions
can be just declared inside the class and defined outside. While defining these functions outside the
class body, we have to link the definition of the function to its declaration in the class. This is done with
the help of a scope resolution operator.
Syntax:
return_type class_name :: function_name ( argument declaration )

Function body

© Cranes Varsity V2.1-2020-21 21/ 126


C++ www.cranesvarsity.com

Programming tips

 Visualize the application and decide on what is an important abstraction for the
application. For example, for creation of an employee database the abstraction is the
employee, which can be made a class. The data members of this class can be employee
id, designation, address and the functions could be setting and getting of these values

 Follow the practice that a class member function should never have cin’s and cout’s.

 Convention says that the class name should start with an upper case letter

A better programming practice is to follow the layered approach.i.e header file, application layer and
library layer. Header file should contain all structure / class declarations, global function declarations,
macros and other symbolic literals. Library layer should contain the definitions of all the functions
declared in the header file and application layer is the user interacting part.
Modifying the above program using layered approach.

//Student.h – Header file


//Program to illustrate the encapsulation of Student

#include <iostream>
using namespace std;

class Student
{
private:
char Name[20];
int College_Id;
char Course[20];
float Total_Marks;
float Fees;
public:
void Enroll_Student(char *, int, char *, float);
void Set_Marks(float);
float Calculate_Percentage(int);
};
//Student_lib.cpp – Library layer
#include "student.h"

void Student :: Enroll_Student(char *n, int i, char *c, float f)


{
strcpy(Name, n);
College_Id = i;
strcpy(Course, c);
Fees = f;
Total_Marks = 0;
}
void Student :: Set_Marks(float total)
{
Total_Marks = total;
}

© Cranes Varsity V2.1-2020-21 22/ 126


C++ www.cranesvarsity.com

float Student :: Calculate_Percentage(int no_subjects)


{
return (Total_Marks / no_subjects);
}

//Student_app.cpp – Application layer

#include "student.h"

int main()
{
char name[20];
int id;
char course[20];
float fees;

Student Anu;

cout << "Enter the name, id, course selected and fees" << endl;
cin >> name >> id >> course >> fees;
Anu.Enroll_Student(name, id, course, fees);

float marks;
int no_subjects;

cout << "Enter total marks and total no of subjects" << endl;
cin >> marks >> no_subjects;
Anu.Set_Marks(marks);
cout << Anu.Calculate_Percentage(no_subjects) << endl;
return 0;
}

Memory of an object

The memory occupied by an object is equal to the sum of its data members. Functions are stored in
the text segment (equivalent machine instructions). It is important that we distinguish between the way
the objects look at functions and data members of a class

 Each object gets its own copy of the data member


 All access the same function definition present in the code segment

In other words, data is per object whereas functions are shared. For the above class student, if three
objects are created, lets say Anu, Vinu and Sonu, the memory organization will be :

© Cranes Varsity V2.1-2020-21 23/ 126


C++ www.cranesvarsity.com

Anu Vinu Sonu

Name Name Name

College_Id College_Id College_Id


Course Course Course

Total_Marks Total_Marks Total_Marks

Fees Fees Fees

Enroll_Student ()

Set_Marks()

Calculate_Percentage()

We know by now that each object gets its own set of data members, but all objects of a class share a
single set of member functions i.e. a single copy of each functions exists The question then is, if only
one copy of each member function exists and is used by multiple objects, how does the function
understands which object to be acted upon ?
For this, the compiler supplies an implicit pointer along with the functions, named as “this pointer”
which always points to the object that is invoking the function.

“This” pointer is defined as an implicit pointer supplied by the compiler, which always points
to the object, which is invoking the member function of a class.

Few more applications of this pointer is

 Resolving ambiguities between formal parameters and data members


 Returning invoking objects from the member functions
 Concatenating function calls

© Cranes Varsity V2.1-2020-21 24/ 126


C++ www.cranesvarsity.com

// Program to illustrate all the above applications


//student.h – header file
#include <iostream>
using namespace std;

class Student
{
private:
char Name[20];
int College_Id;
char Course[20];
float Total_Marks;
float Fees;
public:
void Enroll_Student(char *, int, char *, float);
Student& Set_Marks(float);
float Calculate_Percentage(int);
};
//student_lib.cpp – Library layer

#include "student.h"

void Student :: Enroll_Student(char *n, int i, char *c, float f)


{
strcpy(Name, n);
College_Id = i;
strcpy(Course, c);
Fees = f;
Total_Marks = 0;
}

Student& Student :: Set_Marks(float Total_Marks)


{
this -> Total_Marks = Total_Marks;
return *this;
}

float Student :: Calculate_Percentage(int no_subjects)


{
return (Total_Marks / no_subjects);
}

//Student_app.cpp – Application layer

#include "student.h"

int main()
{
char name[20];
int id;
char course[20];
float fees;

© Cranes Varsity V2.1-2020-21 25/ 126


C++ www.cranesvarsity.com

Student Anu;

cout << "Enter the name, id, course selected and fees" << endl;
cin >> name >> id >> course >> fees;

Anu.Enroll_Student(name, id, course, fees);

float marks;
int no_subjects;

cout << "Enter total marks and total no of subjects" << endl;
cin >> marks >> no_subjects;

cout<<Anu.Set_Marks(marks).Calculate_Percentage(no_subjects)<<endl;

return 0;
}

Note : Find the changes made in the program and analyze how it is working.

**********

© Cranes Varsity V2.1-2020-21 26/ 126


C++ www.cranesvarsity.com

CHAPTER - 4
CLASS INTERNALS – CONSTRUCTORS AND DESTRUCTORS

Constructors

Consider the abstraction and encapsulation of the course which is part of our school management
system.

Course

- Course_Name: String
- Course_Id: Integer
- Duration: Integer
- No_Modules: Integer
- Course_Fees: float

+ Set_Details (): void


+ Include_Modules (): void
+ Change_Fees (): void
+ Ret_Course (): char*
+ Ret_Duration (): int
+ Ret_Modules (): int
+ Ret_Fees (): float

//Program for encapsulation of Course


//course.h – Header file

#include <iostream>
using namespace std;

class Course
{
private:
char Course_Name[20];
int Course_Id;
int Duration;
int No_Modules;
float Course_Fees;

public:
void Set_Details(char*, int, int, int, float);
void Include_Modules(int);
void Change_Fees(float);
char* Ret_Course();
int Ret_Duration();
int Ret_Modules();
float Ret_Fees();
};

© Cranes Varsity V2.1-2020-21 27/ 126


C++ www.cranesvarsity.com

//course_lib.cpp – Library layer

#include "course.h"
void Course :: Set_Details(char *c, int i, int d, int n, float f)
{
strcpy(Course_Name, c);
Course_Id = i;
Duration = d;
No_Modules = n;
Course_Fees = f;
}
void Course :: Include_Modules(int m)
{
No_Modules += m;
}
void Course :: Change_Fees(float f)
{
Course_Fees = f;
}
char* Course :: Ret_Course()
{
return Course_Name;
}
int Course :: Ret_Duration()
{
return Duration;
}
int Course :: Ret_Modules()
{
return No_Modules;
}
float Course :: Ret_Fees()
{
return Course_Fees;
}

//course_app.cpp – Application layer


#include "course.h"
int main()
{
Course Electronics;

Electronics.Set_Details("Electronics", 1, 180, 8, 12000);


Electronics.Include_Modules(5);
Electronics.Change_Fees(15000);

cout << "----Course Details----" << endl;


cout << Electronics.Ret_Course() << endl;
cout << Electronics.Ret_Duration() << endl;
cout << Electronics.Ret_Modules() << endl;
cout << Electronics.Ret_Fees() << endl;
return 0;
}

© Cranes Varsity V2.1-2020-21 28/ 126


C++ www.cranesvarsity.com

In the encapsulation of Course, a Set_Details() function is defined which should be explicitly called
each and every time after the creation of an object for these classes and these functions are
responsible for assigning valid values to the attributes of the objects. A failure to invoke these
functions will result in an undefined state of an object i.e., object’s attributes initialized to garbage.

C++ compiler provides a set of special member functions for a class which gets invoked automatically
at the time of instantiation. These special member functions are termed as constructors.Constructors
are special member function which takes care of initialization of an object’s data members and
performs resource allocation if needed. The resource can be heap memory, files or any system wide
resources. Certain features associated with a constructor are as below
 A constructor is used to initialize an object when it is created instead of making a separate
call to a function.
 A constructor shares its name with the class.
 The call to constructor is implicit.
 A constructor should be defined under the public access specification (try defining it under
the private access specification)
 A constructor is invoked separately for each object created.
 A constructor does not have return type.

There are mainly three types of constructors :


 Default constructor: Constructor with no arguments
 Parameterized constructors: Constructor with arguments. With this type of constructors we
are able to initialize our objects at definition time with our own values
 Copy constructor: It is used to declare and initialize an object from an already existing
object. It takes a reference to an object as an argument.

A single class can have multiple constructors defined inside it. ie) we can write more than one
constructor functions which share the same name inside a class. But each and every constructor
should differ in terms of the arguments they take. The arguments should differ either in terms of
number of arguments, or in terms of type of arguments or in terms of order of arguments. And when
the arguments are pointers or references they can differ in their constantness or/and volatileness.

The above discussion directly implies that constructor functions are working based on function
overloading principles.

Points to remember:
 When no constructor is present in a class the compiler provides you with a default
constructor. In addition to the default constructor, compiler also provides a copy
constructor as and when needed.
 Once we declare any constructor other than the default constructor it is necessary to
define the implicit constructor (default or zero argument constructors) as well.

Lets try to modify our above encapsulation for course using default and parameterized constructors.
Encapsulation of Course with constructors

© Cranes Varsity V2.1-2020-21 29/ 126


C++ www.cranesvarsity.com

//course.h – Header file


#include <iostream>
using namespace std;
class Course
{
private:
char Course_Name[20];
int Course_Id;
int Duration;
int No_Modules;
float Course_Fees;
public:
Course();
Course(char*, int, int, int, float);
void Include_Modules(int);
void Change_Fees(float);
char* Ret_Course();
int Ret_Duration();
int Ret_Modules();
float Ret_Fees();
};

//course_lib.cpp – Library layer


#include "course.h"
Course :: Course()
{
strcpy(Course_Name, "Electronics");
Course_Id = 1;
Duration = 180;
No_Modules = 6;
Course_Fees = 12000.00;
}
Course :: Course(char *n, int i, int d, int m, float f)
{
strcpy(Course_Name, n);
Course_Id = i;
Duration = d;
No_Modules = m;
Course_Fees = f;
}
void Course :: Include_Modules(int m)
{
No_Modules += m;
}
void Course :: Change_Fees(float f)
{
Course_Fees = f;
}
char* Course :: Ret_Course()
{
return Course_Name;
}

© Cranes Varsity V2.1-2020-21 30/ 126


C++ www.cranesvarsity.com

int Course :: Ret_Duration()


{
return Duration;
}
int Course :: Ret_Modules()
{
return No_Modules;
}
float Course :: Ret_Fees()
{
return Course_Fees;
}

//course_app.cpp – Application layer


#include "course.h"

int main()
{
Course Electronics;

cout << "----Course Details----" << endl;


cout << Electronics.Ret_Course() << endl;
cout << Electronics.Ret_Duration() << endl;
cout << Electronics.Ret_Modules() << endl;
cout << Electronics.Ret_Fees() << endl;

Course Computers("CSE", 2, 100, 12, 15000.00);

cout << "----Course Details----" << endl;


cout << Computers.Ret_Course() << endl;
cout << Computers.Ret_Duration() << endl;
cout << Computers.Ret_Modules() << endl;
cout << Computers.Ret_Fees() << endl;

return 0;
}

In the above program, the data members of the class is getting initialized in the body of the
constructor. Another way of initialization using constructor is using initializer list.

Syntax:
class class_name
{
data_type data1;
data_type data2;
public:
class_name():data1(value1),data2(value2)
};
Summarizing
Constructors are special member functions of the class which gets invoked automatically at the
time of creation of objects and which takes up the responsibility of initialization of the objects.

Destructor

© Cranes Varsity V2.1-2020-21 31/ 126


C++ www.cranesvarsity.com

The complement of a constructor is a destructor. If not defined by the user, compiler wills supply one
destructor for each program implicitly. In many circumstances, an object will need to perform some
action or actions when it is destroyed.

Local objects are created when the control enters the block and are destroyed when the control goes
out of the block. Global objects are destroyed when the program terminates.

When an object is destroyed, its destructor function (compiler supplied / user defined) is automatically
called. In C++, it is the destructor function that handles the clean up operations. A destructor has the
same name as the constructor but is preceded by a ~.

Note:
 Constructor does not allocate memory to an object
 Destructor does not remove any object from memory
 There is only one type of destructor. That which takes zero arguments
 The destructor is called when the created object is coming out of the scope.

The actual purpose of destructors comes into picture only when the attribute of the class encounters
dynamic memory allocation.

Dynamic Memory Allocation

To give a user more control over creating and deleting memory for any type of variable, both C and C+
+ provides facility for performing dynamic memory allocation. In C, dynamic memory allocation is
achieved with the help of the functions like malloc ( ) and dynamic memory deallocation using free ( ).
They have certain drawbacks as listed below

 Requires type casting.


 Requires the sizeof operator
 Cannot initialize the allocated memory with a user given value at the time of allocation
 Complex syntax

In C++, the dynamic memory allocation is still possible using malloc and free for backward
compatibility with C. C++ however prefers to make memory allocation simpler by making use of two
operators: new and delete.

The operator new is a substitute for malloc () and operator delete is a substitute for free(). The syntax
for new and delete to allocate and de-allocate memory for a single variable is:

Syntax:
Data_type pointer_var = new data_type;
delete pointer_var;

There are separate operators for allocating and deleting the memory for an array. The syntax of new
and delete for allocating and de-allocating an array of memory is

Data_type pointer_var = new data_type[No of bytes]


Delete [] pointer_var;

The new [] allots some number bytes that is held by the pointer. The pointer holds the address of the
first memory location

© Cranes Varsity V2.1-2020-21 32/ 126


C++ www.cranesvarsity.com

*pointer_variable

Here, using “delete”, will only de-allocate the 1 st block of memory. The result is a condition called
memory leak, where the memory is reserved but is in-accessible. In the above case, memory is still
held from the second block onwards but is inaccessible since the pointer has de-allocated the 1 st block
of memory.

To avoid such problems, we make use of a special version of delete [ ] which de-allocates the entire
block of memory.

The delete operator must be used only with a valid pointer previously allocated by using new. Using
any other type of pointer with delete is undefined and may cause serious problems. The new and
delete operators have several advantages over malloc and free:
 No type casting needed – automatically returns a pointer to the type for which memory is
being allocated.
 No sizeof operator required – internally calculates the size of the data type.
 new and delete calls the constructor and destructor functions internally when memory is
allocated for object dynamically.
 New can be used to initialize the allocated memory. The syntax for that is
Data_type pointer_var = new data_type (Value);

Note: With new, memory initialization is possible only for a single location. but not for array of
locations.
Difference between malloc and new

 Simpler syntax.

 Another difference between new and malloc is , when malloc fails it returns a NULL pointer
where as when new fails it returns a run time error or more specifically termed as exception.
Since we will not be dealing with handling the exceptions in our module, we will be using the
old version of new named as no throw version which will return us NULL on failure.

new (nothrow) datatype.


//Program to illustrate dynamic memory allocation
//student.h – Header file
#include <iostream>
using namespace std;
class Student
{
private:
char *Name;
int College_Id;
char *Course;
float Total_Marks;
float Fees;
public:
Student();
Student(char *, int, char *, float);
void Set_Marks(float);
float Calculate_Percentage(int);
~Student();
};
© Cranes Varsity V2.1-2020-21 33/ 126
C++ www.cranesvarsity.com

//student_lib.cpp – Library file


#include "student.h"

Student :: Student() {}

Student :: Student(char *n, int i, char *c, float f)


{
Name = new (nothrow) char[strlen(n) + 1];
if (Name == NULL)
exit(1);
strcpy(Name, n);
College_Id = i;
Course = new (nothrow) char[strlen(c) + 1];
if (Course == NULL)
exit(1);
strcpy(Course, c);
Fees = f;
Total_Marks = 0;
}
void Student :: Set_Marks(float total)
{
Total_Marks = total;
}
float Student :: Calculate_Percentage(int no_subjects)
{
return (Total_Marks / no_subjects);
}
Student :: ~Student()
{
delete [ ] Name;
delete [ ] Course;
}
//student_app.cpp – Application layer

#include "student.h"
int main()
{
char name[20];
int id;
char course[20];
float fees;

cout << "Enter the name, id, course selected and fees" << endl;
cin >> name >> id >> course >> fees;

Student *Anu;
Anu = new (nothrow) Student(name, id, course, fees);
if (Anu == NULL)
exit(1);
int no_subjects;
float marks;

© Cranes Varsity V2.1-2020-21 34/ 126


C++ www.cranesvarsity.com

cout << "Enter total marks and total no of subjects" << endl;
cin >> marks >> no_subjects;

Anu -> Set_Marks(marks);


cout << Anu -> Calculate_Percentage(no_subjects) << endl;

return 0;
}

**********

© Cranes Varsity V2.1-2020-21 35/ 126


C++ www.cranesvarsity.com

CHAPTER-5
CLASS INTERNALS – COPY CONSTRUCTORS

Since the compiler supplies a copy constructor by itself, what then is the need to define a new copy
constructor? In most cases, the compiler-supplied definition is sufficient. But when we have pointers
and dynamic memory allocation in the class, then it becomes necessary to define the copy
constructor. Consider the following code.

//Encapsulation of Student
//student.h – Header file

#include <iostream>
using namespace std;

class Student
{
private:
char *Name;
int College_Id;
char *Course;
float Total_Marks;
float Fees;
public:
Student();
Student(char *, int, char *, float, float = 0);
void Set_Marks(float);
float Calculate_Percentage(int);
char* Ret_Name();
int Ret_Id();
char* Ret_Course();
~Student();
};

void Student_Details(Student);
//student_lib.cpp – Library layer

#include "student.h"
#include "student.h"

Student :: Student() { }

© Cranes Varsity V2.1-2020-21 36/ 126


C++ www.cranesvarsity.com

Student :: Student(char *n, int i, char *c, float f, float m)


{
Name = new (nothrow) char[strlen(n) + 1];
if (Name == NULL)
exit(1);
strcpy(Name, n);
College_Id = i;
Course = new (nothrow) char[strlen(c) + 1];
if (Course == NULL)
exit(1);
strcpy(Course, c);
Fees = f;
Total_Marks = m;
}
void Student :: Set_Marks(float total)
{
Total_Marks = total;
}
float Student :: Calculate_Percentage(int no_subjects)
{
return (Total_Marks / no_subjects);

}
char* Student :: Ret_Name()
{
return Name;
}
int Student :: Ret_Id()
{
return College_Id;
}
char* Student :: Ret_Course()
{
return Course;
}
Student :: ~Student()
{
delete [ ] Name;
delete [ ] Course;
}
void Student_Details(Student user)
{
cout << "Student name is " << user.Ret_Name() << endl;
cout << "Student id is " << user.Ret_Id() << endl;
cout << "Course Enrolled is " << user.Ret_Course() << endl;
}

© Cranes Varsity V2.1-2020-21 37/ 126


C++ www.cranesvarsity.com

//student_app.cpp - Application layer


#include "student.h"
int main()
{
Student Anu("Anu", 1, "ECE", 12000, 250);
Student Vinu("Vinu", 2, "CSE", 20000, 400);
cout << "Displaying the details of Anu" << endl;
Student_Details(Anu);
cout << "Displaying the details of vinu" << endl;
Student_Details(Vinu);
cout << "Once more displaying the details of Anu" << endl;
Student_Details(Anu);
return 0;
}

Displaying the details of Anu


Student name is Anu
Student id is 1
Course Enrolled is ECE
Displaying the details of vinu
Student name is Vinu
Student id is 2
Course Enrolled is CSE
Once more displaying the details of Anu
Student name is
Student id is 1
Course Enrolled is

If you notice the output of the program, when the details of Anu is displayed for the second time, the
name field and course field is not displayed or sometimes even a garbage value may be encountered.

The reason for this is, when the object Anu is passed to the function Student_Details() for the first
time, to copy Anu to the new object user, the copy constructor provided by the compiler gets invoked.
This compiler provided copy constructor do a bit by bit copy which results in a memory picture as
shown below

© Cranes Varsity V2.1-2020-21 38/ 126


C++ www.cranesvarsity.com

Stack Heap

Anu

Name 1000

College_Id 1
1000
Course 2000

Total_Marks 250

Fees 12000

User

Name 1000

College_Id 2

Course 2000
2000

Total_Marks 400

Fees 20000

When the local object user comes out of the scope, the destructor of the class Student will be
automatically called which de-allocates the memory allocated dynamically. i.e., locations 1000 and
2000 is de-allocated which indirectly implies that the pointers of Anu is now pointing to memory
locations which are already freed.
The bit by bit copy or member by member copy done by the copy constructor is technically termed as
shallow copy. To overcome this problem, a programmer can supply the definition of the copy
constructor in which, each and every pointer gets itw own memory location and then the contents of
one location are copied on to the other. This is known as deep copy.
//Encapsulation of Student with copy constructor
//student.h – Header file
#include <iostream>
using namespace std;
class Student
{
private:
char *Name;
int College_Id;
char *Course;
float Total_Marks;
float Fees;

© Cranes Varsity V2.1-2020-21 39/ 126


C++ www.cranesvarsity.com

public:
Student();
Student(char *, int, char *, float, float = 0);
Student(const Student&);
void Set_Marks(float);
float Calculate_Percentage(int);
char* Ret_Name();
int Ret_Id();
char* Ret_Course();
~Student();
};

void Student_Details(Student);

//student_lib.cpp – Library layer


#include "student.h"
#include "student.h"

Student :: Student() {}

Student :: Student(char *n, int i, char *c, float f, float m)


{
Name = new (nothrow) char[strlen(n) + 1];
if (Name == NULL)
exit(1);
strcpy(Name, n);
College_Id = i;
Course = new (nothrow) char[strlen(c) + 1];
if (Course == NULL)
exit(1);
strcpy(Course, c);
Fees = f;
Total_Marks = m;
}

Student :: Student(const Student &obj)


{
Name = new (nothrow) char[strlen(obj.Name) + 1];
if (Name == NULL)
exit(1);
strcpy(Name, obj.Name);
Course = new (nothrow) char[strlen(obj.Course) + 1];
if (Course == NULL)
exit(1);
strcpy(Course, obj.Course);
College_Id = obj.College_Id;
Fees = obj.Fees;
Total_Marks = obj.Total_Marks;
}
void Student :: Set_Marks(float total)
{
Total_Marks = total;
}

© Cranes Varsity V2.1-2020-21 40/ 126


C++ www.cranesvarsity.com

float Student :: Calculate_Percentage(int no_subjects)


{
return (Total_Marks / no_subjects);

}
char* Student :: Ret_Name()
{
return Name;
}
int Student :: Ret_Id()
{
return College_Id;
}
char* Student :: Ret_Course()
{
return Course;
}
Student :: ~Student()
{
delete [ ] Name;
delete [ ] Course;
}
void Student_Details(Student user)
{
cout << "Student name is " << user.Ret_Name() << endl;
cout << "Student id is " << user.Ret_Id() << endl;
cout << "Course Enrolled is " << user.Ret_Course() << endl;
}

//student_app.cpp
#include “student.h”
int main()
{
Student Anu("Anu", 1, "ECE", 12000, 250);
Student Vinu("Vinu", 2, "CSE", 20000, 400);
cout << "Displaying the details of Anu" << endl;
Student_Details(Anu);
cout << "Displaying the details of vinu" << endl;
Student_Details(Vinu);
cout << "Once more displaying the details of Anu" << endl;
Student_Details(Anu);
return 0;
}

© Cranes Varsity V2.1-2020-21 41/ 126


C++ www.cranesvarsity.com

Displaying the details of Anu


Student name is Anu
Student id is 1
Course Enrolled is ECE
Displaying the details of vinu
Student name is Vinu
Student id is 2
Course Enrolled is CSE
Once more displaying the details of
Anu
Student name is Anu
Student id is 1
Course Enrolled is ECE

Now the memory picture will be modified as shown below

STACK HEAP

ANU

Name 1000

College_Id 1

Course 1500
1000
Total_Marks 250
Fees 12000

USER 1500

Name
User2000
College_Id 2

Course 2500
2000
Total_Marks 400

Fees 20000

2500

Now when user comes out of the scope, destructor de-allocates 2000 and 2500 which doesn’t affect
the object Anu. So the output will be modified as

Note: Try to find out why the argument of copy constructor is a constant reference object of the class.

© Cranes Varsity V2.1-2020-21 42/ 126


C++ www.cranesvarsity.com

The situations where a copy constructor gets invoked is

 When passing an object by value to a function (Above program illustrates it)


 When returning an object by value from a function
 While instantiating new object from an existing object

Summarizing :

The compiler, by default, provides four member functions. These are


 Default constructor
 Copy constructor
 Destructor
 Overloaded assignment operator

Good to know

Consider the following program

//student.h – Header file of student


#include <iostream>
using namespace std;

class Student
{
private:
char Name[20];
public:
Student();
Student(char *);
};

//student_lib.cpp – Library layer


#include "student.h"

Student :: Student()
{
strcpy(Name, "");
}
Student :: Student(char *n)
{
strcpy(Name, n);
}
//student_app.cpp – Application layer
#include "student.h"
int main()
{
Student Anu = "Anushri";
Student Vinu;
Vinu = "Vinukumar";

return 0;
}

© Cranes Varsity V2.1-2020-21 43/ 126


C++ www.cranesvarsity.com

In the main, we can see an invalid conversion from built in string to a user defined object. Even though
it is invalid, the compiler makes the above statements work by considering the one argument
constructor in the class as a conversion function or more technically conversion constructor which
converts built in string to user defined object.

In order to avoid these type of situations, when ever we have a single argument constructor in the
class, a keyword “explicit” is attached with it. So modifying the above program :

//student.h – Header file of student


#include <iostream>
using namespace std;

class Student
{
private:
char Name[20];
public:
Student();
explicit Student(char *);
};

//student_lib.cpp – Library layer


#include "student.h"

Student :: Student()
{
strcpy(Name, "");
}

Student :: Student(char *n)


{
strcpy(Name, n);
}

//student_app.cpp – Application layer


#include "student.h"

int main()
{
Student Anu = "Anushri";
Student Vinu;
Vinu = "Vinukumar";

return 0;
}

Now if you try to compile the program, compiler explicitly throws you an error telling that the above
conversion is an invalid conversion.

© Cranes Varsity V2.1-2020-21 44/ 126


C++ www.cranesvarsity.com

CLASS INTERNALS – STATIC AND CONSTANTS IN C++

Static

The “static” keyword is used in the global scope or the local scope in C++ in a manner exactly similar
to C.

 In the global scope, it is used along with both data and functions. In both the cases, it
helps enforce “internal linkage”. In fact usage of static key word is the only way that we
can enforce a small amount of data hiding in C.
 In the local scope, a static variable is used to retain its value between function calls.

The following code snippet should refresh the use of static keyword.

//Program to illustrate the usage of simple static variables


#include <iostream>
using namespace std;

static int global_count = 0; //can be accessed only in the same file

void fun_1()
{
global_count++;
//Globa_count++;
}
void fun_2()
{
int local_count = 0; //retains values between function calls
global_count++;//updates the last value
local_count++;
cout << "global = " << global_count << endl;
cout << "local = " << local_count << endl;
}
int main()
{
cout << "Test for static" << endl;
fun_1();
fun_2();
}

Static in a class

Additionally, in C++ static can be used inside a class as “static data members” and “static member
functions”.

Static Data

Static data is used when different objects of the same class have to share information or update a
common parameter. Static data can be under any of the access specifiers

 public: behaves exactly like global static variables. Can be accessed from all parts of the
program

© Cranes Varsity V2.1-2020-21 45/ 126


C++ www.cranesvarsity.com

 private: is private to the class. Is used by different objects of the class to share
information.
 protected: will be taken with inheritance

Static data is a shared data. Therefore, there is only one copy of the static data member of a
class. This is irrespective of the access specification under which it is defined. Hence static
data members are declared in the class but defined outside the class using class name and the
scope resolution operator.

Syntax:

class class_name
{
private:
static data_type var1_name; //private static data declaration
protected:
static data_type var2_name; //protected static data declaration
public:
static data_type var3_name; //public static data declaration

};
Data_type class_name : : var1_name; // definition of private static data
Data_type class_name : : var2_name; // definition of protected static data
Data_type class_name : : var3_name; // definition of public static data

Note:
 As above, the access specifications, does not matter. All static data members need to be
defined outside the class.
 Not declaring static variables outside will cause linker error.

Static functions

Static member functions have class scope. Unlike the non-static member functions, these functions
have no implicit this argument; therefore, they can use only static data members and enumerators
directly. Static member functions can be accessed without using an object of the corresponding class
type. Static member functions of a class are independent of objects.

 Static member functions cannot access non-static data members of a class


 They can be declared and defined separately.
 If defined outside the class, the static key-word is required only in the class declaration.

Syntax
class class_name
{
--------
--------
public:
-------
static return_type function_name(arg list);
};

© Cranes Varsity V2.1-2020-21 46/ 126


C++ www.cranesvarsity.com

return_type class_name : : function_name (arg list)


{
Statements;
}

void Student
Static::member functions cannot
Enroll_Student(char *n, int i)access non-static data members of a class since the
memory of the non-static data is dependent on object creation.
{
 Static member functions do not have this pointer.
if(Student :: Check_Strength()) //instead of this the below one can be replaced
 We can call static member functions and data independent of the object with the help of the
if( ! Student::Check_Strength()
class )
name and the scope resolution operator.
{
Student *New_Student;
try
The following { is a simplified abstraction of Student illustrating the use of static variables and static
functions: New_Student = new Student(n, i);
Max_Students++;
//Program to illustrate the use of static variables and static functions
cout <<
// student.h – Header file"Successfully enrolled" << endl;
delete
#include <iostream> New_Student;
using namespace std;
}
const int Class_Strength = 25;
class Student catch(bad_alloc obj)
{ {
char cout << obj.what() << endl;
Name[20];
cout << "Enrolling new student failed" << endl;
int College_Id;
}static int Max_Students;
}public:
else Student();
{ Student(char *n, int C);
cout
static<< "Maximum
bool class strength acheived" << endl;
Check_Strength();
cout <<
static "Have
void to wait for next year's
Enroll_Student(char*, int); admissions" << endl;
} static void Leave_School();
}};
void Student :: Leave_School()
{//student_lib.cpp – Library layer
cout << "Your certificates are returned" << endl;
Max_Students--;
#include "student.h"
}
//student_app.cpp – Application layer
int Student :: Max_Students;
#include "student.h"
int main() Student() {}
Student::
{
Studentchar:: option;
Student(char *n, int i)
{ cout << "Enroll : press e" << endl;
cout << "Leaven);: press l" << endl;
strcpy(Name,
College_Id = i;
} cin >> option;
switch(option)
bool Student :: Check_Strength()
{ {
case 'e' :
if(Class_Strength < Max_Students)
{return false;
else char name[20];
returninttrue;
id;
} cout << "Enter the name and id of the student" << endl;
cin >> name >> id;
Student :: Enroll_Student(name, id);
break;
}
© Cranes Varsity V2.1-2020-21 47/ 126
C++ www.cranesvarsity.com

case 'l' :
Student :: Leave_School();
break;
default :
break;
}
}

Note : Analyze the program and try to understand the working.

Const in C++

C, as a programming language is not very “const-correct”. For instance,


 Most compilers will not object if constants are not initialized
 Some compilers allow you to change the value of an un-initialized const
 These problems do not occur in C++

Constants in Class

Constants are used in a class as const data members and const member functions.

Const data

Data members of a class can be qualified as constants.

Writing data members in a class is just declaration. Constants, have to be initialized as soon
as they are declared. Since it cannot be done at the time of declaration, member initializer list
have to used for this.

Syntax:
class class_name
{
const data_type1 data1;
const data_type2 data2;
public:
class_name():data1(val1),data2(val2)
{
-----
}
class_name(arg1, arg2) : data1(arg1), data2(arg2)
{
------
}

};

NOTE:
 The initializer list can be used to initialize both const and non-const data.
 Multiple data members’ initialization is separated by comma operator
 It is extremely powerful programming technique, since it allows different constant values to
be set for different objects of the same class.

© Cranes Varsity V2.1-2020-21 48/ 126


C++ www.cranesvarsity.com

Constant member functions

Only the member functions of a class can be made a constant member functions.

Const member functions can access/use all the data members of a class but cannot change
any of the values.

 A const function is also called an “accessor”, i.e. It can access all the data members of a
class but cannot modify any of these values
 The exceptions to this rule are the static data members and the location pointed by the
pointer data members of the class.
 If the declaration and definition of const member function are separate then the const key
word (appears) should be used in both declaration and definition after the argument list of
the function
 In constant member functions, this pointer will be acting as a constant pointer to a
constant object. Ie) there can be a constant and non constant version of the same function

Syntax
class class_name
{
data_type1 data1;
data_type2 data2;
-------;
public:
------
------
return_type function_name(argument list) const ;
};

return_type class_name::fucntion_name(argument list) const


{
----------
---------
}

Constant Objects

User defines data types – objects – can be qualified constants exactly like the built-in data types.

 A constant object can make a call to constant member functions of the class
 The exception to this rule is the constructor that will be invoked when an object is
created and the destructor will be invoked (that has to be called) when the object is
being destroyed.
 The values of the data members of a const object cannot be changed.

Syntax:

const class_name object_name;

© Cranes Varsity V2.1-2020-21 49/ 126


C++ www.cranesvarsity.com

//Encapsulation of Course modified with constant data and function members


//course.h – Header file
#include <iostream>
using namespace std;
class Course
{
private:
char Course_Name[20];
const int Course_Id;
int Duration;
int No_Modules;
float Course_Fees;
public:
Course(char *, int, int = 180, int = 6, float = 12000.00);
void Include_Modules(int);
void Change_Fees(float);
const char* Ret_Course() const;
int Ret_Duration() const;
int Ret_Modules() const;
float Ret_Fees() const;
};
//course_lib.cpp – Library layer
#include "course.h"
Course :: Course(char *c, int i, int d, int n, float f) : Course_Id(i)
{
strcpy(Course_Name, c);
Duration = d;
No_Modules = n;
Course_Fees = f;
}
void Course :: Include_Modules(int m)
{
No_Modules += m;
}
void Course :: Change_Fees(float f)
{
Course_Fees = f;
}
const char* Course :: Ret_Course() const
{
return Course_Name;
}
int Course :: Ret_Duration() const
{
return Duration;
}
int Course :: Ret_Modules() const
{
return No_Modules;
}
float Course :: Ret_Fees() const
{
return Course_Fees;
}

© Cranes Varsity V2.1-2020-21 50/ 126


C++ www.cranesvarsity.com

//course_app.cpp – Application layer

#include "course.h"

int main() Output


{
Course Electronics("Electronics", 1); ----Course Details----
Electronics
Electronics.Include_Modules(5);
180
Electronics.Change_Fees(15000); 11
cout << "----Course Details----" << endl; 15000
cout << Electronics.Ret_Course() << endl;
cout << Electronics.Ret_Duration() << endl;
cout << Electronics.Ret_Modules() << endl;
cout << Electronics.Ret_Fees() << endl;

return 0;
}

**********

© Cranes Varsity V2.1-2020-21 51/ 126


C++ www.cranesvarsity.com

CHAPTER - 6
FRIEND KEYWORD AND OPERATOR OVERLOADING

Friends in C++

Any data or function which is declared as private inside a class is not accessible outside the
class. A function which is not a member of the class can access such private data. There are
instances where programmers will need to access the private data from non-member functions and
external classes. C++ offers the concept of “friend” to facilitate such cases.

A class can allow non-member functions and other classes to access its own private data, by
making them as friends. Once a non-member function is declared as a friend, it can access the private
data of the class. Similarly when a class is declared as a friend, the friend class can have access to
the private data of the class, which made it as a friend

Global function as friend

Syntax
class class_name
{
---------
---------
friend return_type function_name(argument);//declaring friend function
};

return_type function_name(arguments)//Definition of friend function


{
Accessing hidden attributes;
}
The above mentioned concept is widely used in the following discussion of operator overloading.

Operator Overloading

Operator overloading allows all the C++ operators to be given new definitions so that they can
be used as easily with user-defined data types as with built-in data types. To write operator overloaded
functions, we can either use member functions or friend functions.

The syntax of operator functions is

return type operator # (Function arguments)


{
//Definition of the function
}

If you notice the above syntax functions names of the operator function have two parts
 The “operator” key word
 The symbol of the operator, represented by the “#” in the syntax of the function.The
operator that we want to overload can substitute the # in the syntax.

Lets see operator overloading of few operators which we commonly use in our programming.

© Cranes Varsity V2.1-2020-21 52/ 126


C++ www.cranesvarsity.com

Binary operators : Binary += as member function:

Lets consider the encapsulation of a Complex number.

Complex

-Real : double
-Imaginary : double

+Complex()
+Complex(double, double)
+ operator+=(const Complex&): Complex&
+ Ret_Real() const : double
+ Ret_Imaginary() const : double

//Program to illustrate overloading of += as member function


//complex.h – Header file
#include <iostream>
using namespace std;

class Complex
{
private:
double Real;
double Imaginary;
public:
Complex();
Complex(double, double);
Complex& operator+=(const Complex&);
double Ret_Real() const;
double Ret_Imaginary() const;
};
//complex_lib.cpp
#include "complex.h"

Complex :: Complex() : Real(0.0), Imaginary(0.0) {}

Complex :: Complex(double r, double i) : Real(r), Imaginary(i) {}

Output :
Complex& Complex :: operator+= (const Complex &obj)
{ Real part of o1 = 1.1
Real += obj.Real; Imaginary part of o1 = 2.2
Imaginary += obj.Imaginary;
return *this;
}
double Complex :: Ret_Real() const
{
return Real;
}

double Complex :: Ret_Imaginary() const


{
return Imaginary;
}

© Cranes Varsity V2.1-2020-21 53/ 126


C++ www.cranesvarsity.com

//complex_app.cpp
#include "complex.h"

int main()
{
Complex o1, o2(1.1, 2.2);

o1 += o2;

cout << "Real part of o1 = " << o1.Ret_Real() << endl;


cout << "Imaginary part of o1 = " << o1.Ret_Imaginary() << endl;

return 0;
}

Binary operators : Binary += as friend function:

Modifying the above encapsulation :


Complex

-Real : double
-Imaginary : double

+Complex()
+Complex(double, double)
+ Ret_Real() const : double
+ Ret_Imaginary() const : double
friend operator+=(Complex&,const Complex&):Complex &

//Program to illustrate overloading of += as friend function


//complex.h – Header file
#include <iostream>
using namespace std;

class Complex
{
private:
double Real;
double Imaginary;
public:
Complex();
Complex(double, double);
double Ret_Real( ) const;
double Ret_Imaginary( ) const;
friend Complex& operator+=(Complex&, const Complex&);
};

© Cranes Varsity V2.1-2020-21 54/ 126


C++ www.cranesvarsity.com

//complex_lib.cpp
#include "complex.h"

Complex :: Complex() : Real(0.0), Imaginary(0.0) {}

Complex :: Complex(double r, double i) : Real(r), Imaginary(i) {}

double Complex :: Ret_Real( ) const


{
return Real;
}

double Complex :: Ret_Imaginary( ) const


{
return Imaginary;
}
Complex& operator+= (Complex &obj1, const Complex &obj2)
{
obj1.Real += obj2.Real;
obj1.Imaginary += obj2.Imaginary;
return obj1;
}

//complex_app.cpp – Application layer


#include "complex.h"

int main()
{
Complex o1, o2(1.1, 2.2);

o1 += o2;

cout << "Real part of o1 = " << o1.Ret_Real() << endl;


cout << "Imaginary part of o1 = " << o1.Ret_Imaginary( ) << endl;

return 0;
}

Output :

Real part of o1 = 1.1


Imaginary part of o1 = 2.2

© Cranes Varsity V2.1-2020-21 55/ 126


C++ www.cranesvarsity.com

Binary operators : Binary + with one operand as an integer


Complex

-Real : double
-Imaginary : double

+Complex()
+Complex(double, double)
+Ret_Real() const : double
+ Ret_Imaginary() const : double
+ operator+(int) : Complex
friend operator+(int, const Complex&) :Complex

//Program to illustrate binary + overloading with one argument as an integer


//complex.h – Header file
#include <iostream>
using namespace std;

class Complex
{
private:
double Real;
double Imaginary;
public:
Complex();
Complex(double, double);
double Ret_Real() const;
double Ret_Imaginary() const;
Complex operator+(int);
friend Complex operator+(int, const Complex&);
};
//complex_lib.cpp – Library layer
#include "complex.h"

Complex :: Complex() : Real(0.0), Imaginary(0.0) {}

Complex :: Complex(double r, double i) : Real(r), Imaginary(i) {}

double Complex :: Ret_Real() const


{
return Real;
}
double Complex :: Ret_Imaginary( ) const
{
return Imaginary;
}
Complex Complex :: operator + (int a)
{
Complex temp;
temp .Real = Real + a;
temp . Imaginary = Imaginary;
return temp;
}

© Cranes Varsity V2.1-2020-21 56/ 126


C++ www.cranesvarsity.com

Complex operator+ (int a, const Complex &obj)


{
Complex temp;
temp.Real = a + obj.Real;
temp.Imaginary = obj.Imaginary;
return temp;
}
//complex_app.cpp – Application layer
#include "complex.h"

int main()
{
Complex o1, o2(1.1, 2.2), o3(3.3, 4.4), o4;

o1 = o2 + 10;
cout << "Real part of o1 = " << o1.Ret_Real() << endl;
cout << "Imaginary part of imaginary = " << o1.Ret_Imaginary() << endl;

o4 = 10 + o3;
cout << "Real part of o4 = " << o4.Ret_Real() << endl;
cout << "Imaginary part of o4 = " << o4.Ret_Imaginary() << endl;

return 0;
}

Output:

Real part of o1 = 1.1


Imaginary part of o1 = 2.2
Real part of o1 = 13.4
Imaginary part of o1 = 4.4

Note : Can you overload ’10 + o3’ as member functions ?

Unary operators – Prefix and Postfix operators :


Overloading the prefix and the postfix operators (++ and --) creates a problem of defining functions
with the same name which doesn’t differ in terms of arguments there by violating function overloading
principles. To overcome this, the postfix function definition have to make use of a dummy argument
which is always an integer.
Modifying the encapsulation of class complex :
Complex

-Real : double
-Imaginary : double

+Complex()
+Complex(double, double)
+ Ret_Real() const :double
+ Ret_Imaginary() const :double
+ operator++(): Complex&
friend operator++(Complex&, int) : Complex

© Cranes Varsity V2.1-2020-21 57/ 126


C++ www.cranesvarsity.com

//Program to illustrate postfix and prefix increment operator overloading


//complex.h
#include <iostream>
using namespace std;

class Complex
{
private:
double Real;
double Imaginary;
public:
Complex();
Complex(double, double);
double Ret_Real() const;
double Ret_Imaginary() const;
Complex& operator++();
friend Complex operator++(Complex&, int);
};

//complex_lib.cpp – Library layer


#include "complex.h"

Complex :: Complex() : Real(0.0), Imaginary(0.0) {}

Complex :: Complex(double r, double i) : Real(r), Imaginary(i) {}

double Complex :: Ret_Real() const


{
return Real;
}

double Complex :: Ret_Imaginary() const


{
return Imaginary;
}
Complex& Complex :: operator++()
{
++Real;
++Imaginary;
return *this;
}
Complex operator++ (Complex &obj, int)
{
Complex temp;
temp.Real = obj.Real++;
temp.Imaginary = obj.Imaginary++;
return temp;
}

© Cranes Varsity V2.1-2020-21 58/ 126


C++ www.cranesvarsity.com

//complex_app.cpp – Application layer


#include "complex.h"

int main()
{
Complex o1, o2(1.1, 2.2), o3(3.3, 4.4), o4;
Output:
o1 = ++o2;
Real part of o1 = 2.1
cout << "Real part of o1 = " << o1.Ret_Real() << endl; Imaginary part of o1 = 3.2
cout << "Imaginary part of o1 = " << o1.Ret_Imaginary() << endl;
Real part of o2 = 2.1
cout << "Real part of o2 = " << o2.Ret_Real() << endl; Imaginary part of o2 = 3.2
cout << "Imaginary part of o2 = " << o2.Ret_Imaginary() << endl;
Real part of o4 = 3.3
o4 = o3++; Imaginary part of o4 = 4.4

cout << "Real part of o4 = " << o4.Ret_Real() << endl; Real part of o3 = 4.3
Imaginary part of o3 = 5.4
cout << "Imaginary part of o4 = " << o4.Ret_Imaginary() << endl;
cout << "Real part of o3 = " << o3.Ret_Real() << endl;
cout << "Imaginary part of o3 = " << o3.Ret_Imaginary() << endl;

return 0;
}

Note : Try to overload prefix using friend and postfix using member also.

Input / Output operators : (Insertion and Extraction operators):

Insertion and extraction always works with two pre-defined objects, cout and cin which belong to
classes ostream and istream respectively. These objects are not capable of invoking the member
functions of our class. So both these operators can be overloaded only using friend functions.

Lets modify the encapsulation of class complex :

Complex

-Real : double
-Imaginary : double

+Complex()
+Complex(double, double)
+ Ret_Real() const : double
+ Ret_Imaginary() const: double
friend operator << (ostream&, const Complex&) : ostream&

friend operator >> (istream&, Complex&) : istream&

© Cranes Varsity V2.1-2020-21 59/ 126


C++ www.cranesvarsity.com

//Program to illustrate << and >> overloading

//complex.h – Header file


#include <iostream>
using namespace std;

class Complex
{
private:
double Real;
double Imaginary;
public:
Complex();
Complex(double, double);
friend ostream& operator<< (ostream&, const Complex&);
friend istream& operator>> (istream&, Complex&);
};
Output:
//complex_lib.cpp – Library layer
#include "complex.h" Enter the real part
1.1
Complex :: Complex() : Real(0.0), Imaginary(0.0) {} Enter the imaginary part
2.2
Enter the real part
Complex :: Complex(double r, double i) : Real(r), Imaginary(i) {}
3.3
Enter the imaginary part
ostream& operator<< (ostream &c, const Complex &o) 4.4
{
c << "Real part is = " << o.Real << endl; Real part is = 1.1
c << "Imaginary part is = " << o.Imaginary << endl; Imaginary part is = 2.2
return c;
Real part is = 3.3
}
Imaginary part is = 4.4

istream& operator>> (istream &c, Complex &o)


{
cout << "Enter the real part" << endl;
c >> o.Real;
cout << "Enter the imaginary part" << endl;
c >> o.Imaginary;
return c;
}
//complex_app.cpp – Application layer
#include "complex.h"

int main()
{
Complex o1, o2;

cin >> o1 >> o2;


cout << o1 << endl;
cout << o2 << endl;

return 0;
}

© Cranes Varsity V2.1-2020-21 60/ 126


C++ www.cranesvarsity.com

Note:
If you notice, the arguments of istream and ostream are passed by reference. This is because
the copy constructor of both these classes are in the protected section of the class.

Overloading Assignment Operator:

For the above class Complex, when an expression ‘o1 = o2’ is encountered, the compiler
provided overloaded assignment operator translates it into
o1.Real = o2.Real;
o1.imaginary = o2.Imaginary;
In other words assignment operator is doing a member wise assignment or shallow copy.
As long as the class doesn’t contain any pointer attribute, compiler provided assignment operator
is adequate. But for a class with pointer attributes, compiler provided assignment operator could create
serious problems.

Lets see a simplified abstraction of Student and analyze the problem.

Student

-Name : String
-College_Id : Integer

+Student()
+Student(char*, int)
+Student(const Student&)
+ Ret_Name() const :const char *
+ Ret_Id() const : int
+~Student()
friend Change_Id(Student) :Student

//Program to illustrate member wise copy done by assignment operator


//student.h – Header file
#include <iostream>
using namespace std;

class Student
{
private:
char* Name;
int College_Id;
public:
Student( );
Student(char *, int);
Student(const Student&);
const char* Ret_Name( ) const;
int Ret_Id() const;
~Student();
friend Student Change_Id(Student);
};

© Cranes Varsity V2.1-2020-21 61/ 126


C++ www.cranesvarsity.com

//student_lib.cpp – Library layer


#include "student.h"

Student :: Student() {}
Student :: Student(char *n, int i)
{
Name = new (nothrow) char[strlen(n) + 1];
if (Name == NULL)
exit(1);
strcpy(Name, n);
College_Id = i;
}
Student :: Student(const Student &s)
{
Name = new (nothrow) char[strlen(s.Name) + 1];
if (Name == NULL)
exit(1);
strcpy(Name, s.Name);
College_Id = s.College_Id;
}
const char* Student :: Ret_Name() const
{
return Name;
}

int Student :: Ret_Id() const


{
return College_Id;
}

Student :: ~Student()
{
delete [ ] Name;
}

Student Change_Id(Student sample)


{
sample.College_Id = 1000;
return sample;
}

//student_app.cpp – Application layer


#include "student.h"

int main()
{
Student Anu("Anushri", 1);
Anu = Change_Id(Anu);

cout << "Name of anu is = " << Anu.Ret_Name() << endl;


cout << "Id of anu = " << Anu.Ret_Id() << endl;

return 0;
}

© Cranes Varsity V2.1-2020-21 62/ 126


C++ www.cranesvarsity.com

If you analyze the memory picture of the above program before the Change_Id function returns:

Stack Heap

Anu

Name : 5000
A n u s h r i \0

College_Id : 1 5000

Sample
A n u s h r i \0
Name : 5500

5500
College_Id : 1000

Temporary object

Name : 6000
A n u s h r i \0

College_Id : 1000
6000

Once the function returns sample, the temporary object created internally corresponding to sample
invokes the compiler provided assignment operator to do the copying between Anu and temporary
object.
i.e Anu = temporary object
when the assignment operator do a member wise copy, the above memory picture becomes :

© Cranes Varsity V2.1-2020-21 63/ 126


C++ www.cranesvarsity.com

Stack Heap

Anu
5000
Name : 6000
A n u s h r i \0

College_Id : 1000

5500
Sample
A n u s h r i \0
Name : 5500

College_Id : 1000

A n u s h r i \0
Temporary object
Name : 6000
6000

College_Id : 1000

Here object Anu and Temporary object is pointing to the same memory location. There are two side
effects in the above program. First of all link to 5000 is lost and secondly when temporary object gets
de-allocated, memory pointed by Anu is also de-allocated. There fore the output of the above program
will be

Output :

Name of anu is =
Id of anu = 1000

So here, the assignment operator have to again overloaded to avoid the member wise or shallow
copy.
Assignment operator along with the following three operators should be overloaded only using
member functions.
 ()
 []
 ->
This is to ensure that the LHS operand is always an object of the same class so that we can avoid any
l-value errors.

Syntax for overloading an assignment operator is :


const ClassName& operator = (const ClassName& RHS object);

© Cranes Varsity V2.1-2020-21 64/ 126


C++ www.cranesvarsity.com

Modifying the encapsulation of class student

Student

-Name : String
-College_Id : Integer

+Student()
+Student(char*, int)
+Student(const Student&)
+ operator=(const Student&) : const Student&
+ Ret_Name() const : const char*
+ Ret_Id() const :int
+~Student()
friend Change_Id(Student) : Student

//Program to illustrate overloading of assignment operator


//student.h – Header file

#include <iostream>
using namespace std;

class Student
{
private:
char* Name;
int College_Id;
public:
Student();
Student(char *, int);
Student(const Student&);
const Student& operator= (const Student&);
const char* Ret_Name() const;
int Ret_Id() const;
~Student();
friend Student Change_Id(Student);
};
//student_lib.cpp – Library layer
#include "student.h"

Student :: Student( ) { }
Student :: Student(char *n, int i)
{
Name = new (nothrow) char[strlen(n) + 1];
if (Name == NULL)
exit(1);
strcpy(Name, n);
College_Id = i;
}

© Cranes Varsity V2.1-2020-21 65/ 126


C++ www.cranesvarsity.com

Student :: Student(const Student &s)


{
Name = new (nothrow) char[strlen(s.Name) + 1];
if (Name == NULL)
exit(1);
strcpy(Name, s.Name);
College_Id = s.College_Id;
}
const Student& Student :: operator = (const Student &obj)
{
if(this == &obj)
return *this;
else
{
delete [ ] Name;
Name = new (nothrow) char[strlen(obj.Name) + 1];
if (Name == NULL)
exit(1);
strcpy(Name, obj.Name);
College_Id = obj.College_Id;
return *this;
}
}
const char* Student :: Ret_Name( ) const
{
return Name;
}
int Student :: Ret_Id( ) const
{
return College_Id;
}
Student :: ~Student()
{
delete [ ] Name;
}
Student Change_Id(Student sample)
{
sample.College_Id = 1000;
return sample;
Output:
}
Name of anu is = Anushri
//student_app.cpp – Application layer Id of anu = 1000

#include "student.h"
int main()
{
Student Anu("Anushri", 1);
Anu = Change_Id(Anu);
cout << "Name of anu is = " << Anu.Ret_Name() << endl;
cout << "Id of anu = " << Anu.Ret_Id() << endl;
return 0;
}

© Cranes Varsity V2.1-2020-21 66/ 126


C++ www.cranesvarsity.com

From all the examples, summarizing few general rules of operator overloading

 At least one of the arguments passed to the overloaded functions has to be a user-defined
data types
 The number of arguments to be passed to these functions is fixed depending on the type of
operator and the type of function(member or friend) performing the overloading

Table giving the rule of number of operators

Unary Binary
Member Zero arguments One arguments
Friend One arguments Two arguments
These rules are strictly implemented. If you change the argument list, then the compiler generates
errors like “too many arguments “or “too few arguments”.

A few operators cannot be overloaded. They are:


 ?:

 .
 .*
 ::
 sizeof()
 4 casting operators
 typeid()
 # and ##

The main reason behind it is.

 ? : Operator which works with three operands at a time .So complexity increases.
 The operators . .* :: operators have name association which the concept of overloading
absurd.
 The operators sizeof(), typeid() and 4 casting operators are already working for user
defined data type.
 # and ## are preprocessor directives , we should not change the actual meaning.

The C++ operators that can be overloaded are

+ - * / % ^ & | ~

! = < > += -= *= /= %=

^= &= |= << >> <<= >>= == !=

<= >= && || ++ -- , ->* ->

() [] new delet new[] delete


e []

© Cranes Varsity V2.1-2020-21 67/ 126


C++ www.cranesvarsity.com

If you notice the concept of operator overloading, you can see that, a single interface (in this
case a single operator) is getting used for multiple functionalities. Or generally speaking, “Single
interface and multiple implementations”. This is technically termed as polymorphism.
We have already come across a similar situation where in we were using single interface and
multiple implementations. That was in function overloading. So function overloading can also be
considered as a kind of polymorphism.
Since in function overloading as well as operator overloading, binding (relating function call to function
definition) is taking place during compile time, we call this type of polymorphism as Compile time
polymorphism.

**********

© Cranes Varsity V2.1-2020-21 68/ 126


C++ www.cranesvarsity.com

CHAPTER 7
GENERIC PROGRAMMING : STANDARD TEMPLATE LIBRARY (STL)

Standard Template Library:

The Standard Template Library (STL) is a general-purpose C++ library of algorithms and data
structures, originated by Alexander Stepanov and Meng Lee. The STL, based on a concept known as
generic programming, is part of the standard ANSI C++ library. The STL is implemented by means of
the C++ template mechanism, hence its name.

While some aspects of the library are very complex, it can often be applied in a very straightforward
way, facilitating reuse of the sophisticated data structures and algorithms it contains.
The STL consists of five main components, 3 of which we'll concentrate on first
 Container: object that is able to keep and administer any type of data.(built in as well as user
defined)
 Algorithm: computational procedure that is able to work on different containers
 Iterator: abstraction of "pointer" or "index" - a means to access the appropriate element.

Container Container

Objects
Objects

Iterator
Iterator
Algorith
m

Algorithm
Iterator

Containers:
Containers are data structures that manage a collection of elements and are responsible for the
allocation and de-allocation of those elements. Containers typically have constructors and destructors
along with operations for inserting and deleting elements.
Elements are stored in containers as whole objects; no pointers are used to access the elements in a
container. This results in containers that are type safe and efficient. The STL provides three categories
of containers:

Sequence containers:

Sequence containers store elements in sequential order. These containers group a finite set of
elements in a linear arrangement. The STL includes class templates for vectors, lists, and deques.

characteristics Advantages disadvantages


vector Relocating, expanding array Quick random access Slow to insert or
Fast insert or delete at end delete from in
between
list Double linked list Quick to insert or delete at Slow random
any location. access
Quick access at both ends

© Cranes Varsity V2.1-2020-21 69/ 126


C++ www.cranesvarsity.com

deque Like vector, but can be accessed Quick random access Slow to insert or
at either end delete from in-
between
Quick access at
both ends

The syntax for creation of object for STL classes is exactly similar to the creation of object in normal
template classes. If you take vector as an example, the syntax will be:
vector <data_type> obj_name;

Associative containers:

Associative Containers provide for fast retrieval of objects from the collection based on keys. The size
of the collection can vary at runtime. The collection is maintained in order, based on a comparison
function object of type Compare (a default template parameter according to the STL standard, but a
non-default template parameter in the current implementation).

There are two kinds of associative containers in STL .Sets and Maps.
characteristics
set Stores only the key objects. Only one key of each value allowed
multiset Stores only the key objects. Multiple key values allowed
map Associates key object with value object. Only one key of each value
allowed.
multimap Associates key object with value object. Multiple key values allowed.
Container adapters:

Container adapters are the special purpose containers, created from the normal containers. The
special containers implemented from container adapters in STL are stacks, queues, and priority
queues.

Adapter based container Implementation Characteristics


stack Can be implemented as Insert (push) and remove
vector, list or deque (pop) at one end only.
queue Can be implemented as list or Insert (push) at one end and
deque remove (pop) at other end.
priority_queue Can be implemented as vector Insert (push) in random order
or deque at one end and remove(pop)
in random order from other
end.

Containers use member functions to perform simpler tasks that are specific to a particular type of
container. The table below shows most commonly used member functions

Name Purpose
size() Returns the number of items in the container
empty() Returns true if the container is empty.
max_size() Returns size of the largest possible container
begin() Returns an iterator to the start of the container, for iterating forward
through the container
end() Returns an iterator to the past-the-end location in the container, used to
end forward iteration.
rbegin() Returns a reverse iterator to the end of the container, for iterating
backward through the container.
rend() Returns a reverse iterator to the beginning of the container, used to end
backward iteration.

Algorithms:

© Cranes Varsity V2.1-2020-21 70/ 126


C++ www.cranesvarsity.com

An algorithm is a function that does something to the objects of the containers. Algorithms in STL are
not member functions or friends of container classes, as they are in earlier container libraries, but are
standalone template functions. They can be used with built in c++ arrays, or with container classes you
create yourself.

Algorithm purpose
find Returns the first element equivalent to a specified value.
count Returns the number of elements that have specified value.
equal Compares the contents of the two containers and returns true if all
corresponding elements are equal.
search Looks for a sequence of values from one container that correspond
with the same sequence in another container
copy Copies a sequence of values from one container to another(or to a
different location in the same container).
swap Exchanges a value in one location with a value in another.
inter_swap Exchanges a sequence of values in one location with a sequence of
values in another location.
fill Copies a value into a sequence of locations.
sort Sorts the values in the container according to a specified order.
merge Combines two sorted ranges of elements to make a larger sorted
range.
accumulate Returns the sum of the elements in a given range.
for_each Executes a specified function for each element in the container.

Iterators:

Iterators are the pointer-like entities that are used to access individual data items in a
container. They are used to move sequentially from element to element, through the container.
You can increment the iterators using the ++ operator and * operator to obtain the value of the
element they point to. In STL iterators are represented by an object of an iterator class.Different
classes of iterators are used with different types of containers.

Iterators are classified into five categories:


1. forward iterators: provide for one-directional traversal of a sequence, expressed with ++.
2. bidirectional iterators: provide for traversal in both directions, expressed with ++ and --.
3. random access iterators provide for bidirectional traversal, plus they can jump to any
arbitarary location.
4. input iterator: can point to an input device( say a file) to read sequential data items into a
container.
5. output iterator: can point to an input device( say a file) and write elements from a
container to the device.

Iterator type Read/write Can be saved Direction access


Random access Read and write Yes Forwared and Random
backward
Bidirectional Read and write Yes Forwared and Linear
backward
Forward Read and write Yes Forward only Linear

Output Write only No Forward only Linear


Input Read only No Forward only

//vector

#include <iostream>
#include <vector>

int main ()

© Cranes Varsity V2.1-2020-21 71/ 126


C++ www.cranesvarsity.com

{
std::vector<int> myints;
std::cout << "0. size: " << myints.size() << '\n';

for (int i=0; i<10; i++) myints.push_back(i);


std::cout << "1. size: " << myints.size() << '\n';

myints.insert (myints.end(),10,100);
std::cout << "2. size: " << myints.size() << '\n';

myints.pop_back();
std::cout << "3. size: " << myints.size() << '\n';

return 0;
}

// List basics

#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lst; // create an empty list
int i;
for(i=0; i<10; i++) lst.push_back(i);
cout << "Size = " << lst.size() << endl;
cout << "Contents: ";
list<int>::iterator p = lst.begin();
while(p != lst.end()) {
cout << *p << " ";
p++;
}
cout << "\n\n";
// change contents of list
p = lst.begin();
while(p != lst.end()) {
*p = *p + 100;
p++;
}
cout << "Contents modified: ";
p = lst.begin();
while(p != lst.end()) {
cout << *p << " ";

p++;
}
return 0;
}

// A simple map demonstration.

#include <iostream>
#include <map>
using namespace std;
int main()
{
map<char, int> m;
int i;

© Cranes Varsity V2.1-2020-21 72/ 126


C++ www.cranesvarsity.com

// put pairs into map


for(i=0; i<26; i++) {
m.insert(pair<char, int>('A'+i, 65+i));
}
char ch;
cout << "Enter key: ";
cin >> ch;
map<char, int>::iterator p;
// find value given key
p = m.find(ch);
if(p != m.end())
cout << "Its ASCII value is " << p->second;
else
cout << "Key not in map.\n";
return 0;
}

//stack

#include <iostream>
#include <stack>
using namespace std;
int main()
{

stack<int> st;
// push three elements into the stack
st.push(1);
st.push(2);
st.push(3);
// pop and print two elements from the stack
cout << st.top() << ‘ ‘:
st.pop();
cout << st.top() << ‘ ‘;
st.pop();
// modify top element
st.top() = 77;
// push two new elements
st.push(4);
st.push(5);
// pop one element without processing it
st.pop();
// pop and print remaining elements
while (!st.empty()) {
cout << st.top() << ‘ ‘:
st.pop();
}
cout << endl;

//queue

#include <iostream>
#include <queue>
#include <string>
using namespace std;
int main()
{
queue<string> q;
// insert three elements into the queue
q.push("These ");

© Cranes Varsity V2.1-2020-21 73/ 126


C++ www.cranesvarsity.com

q.push("are ");
q.push("more than ");
// read and print two elements from the queue
cout << q.front();
q.pop();
cout << q.front();
q.pop();
// insert two new elements
q.push("four ");
q.push("words!");
// skip one element
q.pop();
// read and print two elements
cout << q.front();
q.pop();
cout << q.front() << endl;
q.pop();
// print number of elements in the queue
cout << "number of elements in the queue: " << q.size()
<< endl;
}

//priority queue

#include <iostream>
#include <queue>
using namespace std;
int main()
{
priority_queue<float> q;
// insert three elements into the priority queue
q.push(66.6);
q.push(22.2);
q.push(44.4);
// read and print two elements
cout << q.top() << ‘ ‘:
q.pop();
cout << q.top() << endl;
q.pop();
// insert three more elements
q.push(11.1);
q.push(55.5);
q.push(33.3);
// skip one element
q.pop();
// pop and print remaining elements
while (!q.empty()) {
cout << q.top() << ‘ ‘:
q.pop();
}
cout << endl;
}

// fill algorithm example


#include <iostream> // std::cout
#include <algorithm> // std::fill
#include <vector> // std::vector

int main () {
std::vector<int> myvector (8); // myvector: 0 0 0 0 0 0 0 0

std::fill (myvector.begin(),myvector.begin()+4,5); // myvector: 5 5 5 5 0 0 0 0


std::fill (myvector.begin()+3,myvector.end()-2,8); // myvector: 5 5 5 8 8 8 0 0

© Cranes Varsity V2.1-2020-21 74/ 126


C++ www.cranesvarsity.com

std::cout << "myvector contains:";


for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';

return 0;
}

// count algorithm example


#include <iostream> // std::cout
#include <algorithm> // std::count
#include <vector> // std::vector

int main () {
// counting elements in array:
int myints[] = {10,20,30,30,20,10,10,20}; // 8 elements
int mycount = std::count (myints, myints+8, 10);
std::cout << "10 appears " << mycount << " times.\n";

// counting elements in container:


std::vector<int> myvector (myints, myints+8);
mycount = std::count (myvector.begin(), myvector.end(), 20);
std::cout << "20 appears " << mycount << " times.\n";

return 0;
}

// find example
#include <iostream> // std::cout
#include <algorithm> // std::find
#include <vector> // std::vector

int main () {
// using std::find with array and pointer:
int myints[] = { 10, 20, 30, 40 };
int * p;

p = std::find (myints, myints+4, 30);


if (p != myints+4)
std::cout << "Element found in myints: " << *p << '\n';
else
std::cout << "Element not found in myints\n";

// using std::find with vector and iterator:


std::vector<int> myvector (myints,myints+4);
std::vector<int>::iterator it;

it = find (myvector.begin(), myvector.end(), 30);


if (it != myvector.end())
std::cout << "Element found in myvector: " << *it << '\n';
else
std::cout << "Element not found in myvector\n";

return 0;
}

// copy algorithm example

© Cranes Varsity V2.1-2020-21 75/ 126


C++ www.cranesvarsity.com

#include <iostream> // std::cout


#include <algorithm> // std::copy
#include <vector> // std::vector

int main () {
int myints[]={10,20,30,40,50,60,70};
std::vector<int> myvector (7);

std::copy ( myints, myints+7, myvector.begin() );

std::cout << "myvector contains:";


for (std::vector<int>::iterator it = myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;

std::cout << '\n';

return 0;
}

**********

© Cranes Varsity V2.1-2020-21 76/ 126


C++ www.cranesvarsity.com

CHAPTER - 8
GENERALIZATION - INHERITANCE

Generalization is achieved in C++ using inheritance. It is a mechanism of reusing and extending


existing classes without modifying them, thus producing hierarchical relationships between them. It is a
way to form new classes from existing classes. The newly created classes, known as derived classes
and also known as sub class or child class, inherit attributes and behavior of the existing classes,
referred to as base classes and also known as super class or parent class, It is intended to help reuse
of existing code.

Inheritance is built around a specific relationship between different classes generally read as “is – a”
relationship. For instance, a "fruit" is a generalization of "apple", "orange", "mango" and many others.
We say that fruit is an abstraction of apple, orange, etc. Conversely, we can say that since apples are
fruit (i.e. an apple is-a fruit), they inherit all the properties common to all fruit, such as being a fleshy
container for the seed of a plant

Thus, one of the major advantages of inheritance is that modules with sufficiently similar interfaces can
be commanded by shared controlling code, reducing the complexity of the program. Inheritance
therefore has another view, a dual, called polymorphism, which describes many pieces of code being
controlled by shared control code.

What we have already covered with the compile time polymorphism is the basic understanding of
polymorphism. For all object oriented languages, the most powerful and useful application of
polymorphism is with inheritance with the help of virtual functions. Before we cover run – time
polymorphism, let us understand inheritance.

The main advantages that inheritance gives us is


 Reusability: an exiting class features are inherited or reused to create a new class
 Extendibility: new features can be added to the inherited class without making changes
to the original
 Easy debugging: since addition of code is separate, we can compartmentalize the
debugging hence making it simpler and easier
From inheritance the “protected” access specifier becomes significantly different from the “private”
access specifier. The difference between the two is

 private attributes of a class are inherited but inaccessible in an inherited class.


 protected attributes of a class are inherited as well as accessible in the inherited class.

Creating or deriving a new class using an existing class base is called inheritance. The new class
created is called a Derived class and the existing class used as a base is called a Base class in C++
inheritance terminology. The derived class inherits all the features of the base class and also adds its
own features, i.e. data members and member functions.

Syntax of inheritance

class baseclass_name
{
--------
};
class deriver-class_name : mode of inheritance base-class_name
{
--------
};

© Cranes Varsity V2.1-2020-21 77/ 126


C++ www.cranesvarsity.com

While working with inheritance, we need to take care of the “mode” of inheritance.
The mode can be any of the three
 private
 public
 protected
The mode of inheritance helps us to decide the access specification of a base class member in the
derived class. The table below shows the mode of inheritance and the affect it has in inheritance

Mode of inheritance
Base class Member Private Protected Public
Private Not Accessible Not Accessible Not Accessible
Protected Private Protected Protected
Public Private Protected Public

The above table clearly highlights the difference between the private and the protected access
specifiers. Under no circumstances can the base class private attributes be accessed in the derived
class.

Types of Inheritance

The concept of inheritance is not limited to a single base class. Inheritance can be used to build
complex class hierarchies and extend the uses of inheritance to the hierarchy. There are mainly five
types of inheritance classifications. These are

Single Inheritance

one derived class created from a single base class

class base BASE


{
………….
};
class derived : mode base
{
DERIVED
………………..

};

Let’s consider our college management systems and let’s try to trace out a single inheritance
hierarchy of student

© Cranes Varsity V2.1-2020-21 78/ 126


C++ www.cranesvarsity.com

Student

-Name : String
-College_Id : Integer
-Course: String
-Fees : Float
#Total_Marks : Float

+Enroll_Student(char*, int, char*, float)


+Set_Total_Marks(float):void
+Calculate_Percentage(int):float

Engineering_Student

-Project_Title : String
-Project_Marks : Float

+Set_Project_Title(char *):void
+Set_Project_Marks(float):void
+Update_Total_Marks():void

//Program illustrating single inheritance


//student.h – Header file of student
#include <iostream>
using namespace std;
class Student
{
private:
char Name[20];
int College_Id;
char Course[20];
float Fees;
protected:
float Total_Marks;
public:
void Enroll_Student(char *, int, char *, float);
void Set_Total_Marks(float);
float Calculate_Percentage(int);
};
//student_lib.cpp – Library layer of student
#include "student.h"
void Student :: Enroll_Student(char *n, int i, char *c, float f)
{
strcpy(Name, n);
College_Id = i;
strcpy(Course, c);
Fees = f;
Total_Marks = 0;
}

© Cranes Varsity V2.1-2020-21 79/ 126


C++ www.cranesvarsity.com

void Student :: Set_Total_Marks(float total)


{
Total_Marks = total;
}

float Student :: Calculate_Percentage(int no_subjects)


{

return (Total_Marks / no_subjects);

}
//engg_student.h – Header file of engg_student (derived
class)
#include "student.h"

class Engineering_Student : public Student


{
private:
char Project_Title[20];
float Project_Marks;
public:
void Set_Project_Title(char *);
void Set_Project_Marks(float);
void Update_Total_Marks();
};

//engg_student_lib.cpp – Library layer of engg_student


#include "engg_student.h"

void Engineering_Student :: Set_Project_Title(char *t)


{
strcpy(Project_Title, t);
}
void Engineering_Student :: Set_Project_Marks(float p)
{
Project_Marks = p;
}
void Engineering_Student :: Update_Total_Marks()
{
Total_Marks += Project_Marks;
}
//engg_student_app.cpp – Application layer
#include "engg_student.h"
int main()
{
Engineering_Student Anu;
Anu.Enroll_Student("Anushri", 1, "Engineering", 15000.00);
Anu.Set_Project_Title("Cryptography"); Output:
Anu.Set_Total_Marks(320);
Percentage calculated for Anu = 80
Anu.Set_Project_Marks(80);
Anu.Update_Total_Marks();
cout<<"Percentage calculated for Anu= ";
cout<<Anu.Calculate_Percentage(5) <<endl;
return 0;
}
© Cranes Varsity V2.1-2020-21 80/ 126
C++ www.cranesvarsity.com

Multilevel inheritance Base

Syntax :
Class Base
{
-------
}; Derived1
Class Derived1 : mode Base
{
----------
};
Class Derived2 : mode Derived1
Derived2
{
-------
};
Lets try to extend the student hierarchy for multilevel inheritance

Student

Engineering_Student

Computer_Engg_Student

Note : Try to implement the above mentioned hierarchy by putting attributes and behaviours in each
and every class.

Hierarchical Inheritance

Syntax: Base
class Base
{
----
};
class Derived1 : mode Base
{ Derived1 Derived2
-----
};
Class Derived2 : mode Base
{
------ Derived3 Derived4
};
Class Derived3 : mode Derived1
{
-----
};
Class Derived4 : mode Derived 2
{

© Cranes Varsity V2.1-2020-21 81/ 126


C++ www.cranesvarsity.com

----
};
Extending the students examples with hierarchical inheritance

Student

Engineering_Student Medical_Student

Electronics_Engg Computer_Engg Ayurvedic Homeopathic

Note : Try to implement the above mentioned hierarchy with proper attributes and behaviours.

Multiple Inheritance

one derived class created from two or more base classes

Syntax

BASE1 BASE2
class Base1
{
-------
};
class Base2 DERIVED
{
-------
};
class Derived: mode Base1, mode Base2
{
-------
};
Let’s take the classes electronics engineer and computer engineer in the student hierarchy and try to
extend it for multiple inheritances.

© Cranes Varsity V2.1-2020-21 82/ 126


C++ www.cranesvarsity.com

Electronics_Engg Computer_Engg

#Device_No : Integer #Programming_Lang : String


#Device_Name : String

+Set_Language(char *):void
+Set_Device_Name(char*):void
+Set_Device_No(int):void

Embedded_Engg

+Ret_Device_Name():char*
+Ret_Device_No():int
+Ret_Programming_Lang():char *

//Program to illustrate multiple inheritance of the above class diagram


//elect_engg.h – Header file of Electronics engg
#include <iostream>
using namespace std;

#ifndef __ELECT_H
#define __ELECT_H

class Electronics_Engg
{
protected:
char Device_Name[20];
int Device_No;
public:
void Set_Device_Name(char*);
void Set_Device_No(int);
};
#endif

//elect_engg_lib.cpp – Library layer of electronics engg


#include "elect_engg.h"

void Electronics_Engg :: Set_Device_Name(char *n)


{
strcpy(Device_Name, n);
}

void Electronics_Engg :: Set_Device_No(int n)


{
Device_No = n;
}

© Cranes Varsity V2.1-2020-21 83/ 126


C++ www.cranesvarsity.com

//comp_engg.h – Header file of computer engineer


#include <iostream>
using namespace std;
#ifndef __COMP_H
#define __COMP_H

class Computer_Engg
{
protected:
char Programming_Lang[20];
public:
void Set_Language(char*);
};
#endif

//comp_engg_lib.cpp – Library layer of computer engg


#include "comp_engg.h"
void Computer_Engg :: Set_Language(char *p)
{
strcpy(Programming_Lang, p);
}
//embb_engg.h – Header file of embedded engineer
#include <iostream>
#include "elect_engg.h"
#include "comp_engg.h"
using namespace std;

#ifndef __EMBB_H
#define __EMBB_H

class Embedded_Engg : public Electronics_Engg, public Computer_Engg


{
public:
char* Ret_Device_Name();
int Ret_Device_No();
char* Ret_Programming_Lang();
};
#endif

//embb_engg_lib.cpp – Library layer of embedded engineer


#include "embb_engg.h"
char* Embedded_Engg :: Ret_Device_Name()
{
return Device_Name;
}
int Embedded_Engg :: Ret_Device_No()
{
return Device_No;
}
char* Embedded_Engg :: Ret_Programming_Lang()
{
return Programming_Lang;
}

© Cranes Varsity V2.1-2020-21 84/ 126


C++ www.cranesvarsity.com

//emb_app.cpp – Application layer


#include "embb_engg.h"

int main()
{
Embedded_Engg Anu;
char dev_name[20], prog_lang[20];
int dev_no;

cout << "Enter the device name" << endl;


cin >> dev_name;

Anu.Set_Device_Name(dev_name);

cout << "Enter the device no" << endl;


cin >> dev_no;

Anu.Set_Device_No(dev_no);

cout << "Enter the programming language" << endl;


cin >> prog_lang;

Anu.Set_Language(prog_lang);

cout << "----Displaying the details----" << endl;


cout << "Device name is = " << Anu.Ret_Device_Name() << endl;
cout << "Device no is = " << Anu.Ret_Device_No() << endl;
cout << "Programming lang is = " << Anu.Ret_Programming_Lang() << endl;

return 0;
}

Output:

Enter the device name


PCB
Enter the device no
1020
Enter the programming language
JAVA
----Displaying the details----
Device name is = PCB
Device no is = 1020
Programming lang is = JAVA

NOTE: Each base class should mention the mode of inheritance separately

© Cranes Varsity V2.1-2020-21 85/ 126


C++ www.cranesvarsity.com

Hybrid / Diamond Inheritance

Base
Syntax :

class Base
{
--------
};
Derived1 Derived2
class Derived1: mode Base
{
--------
};
class Derived2 : mode Base
{
--------
};
class Derived: mode Derived1,mode Derived2 Derived
{
-------
};

From the above syntax it is quite clear that the final derived class “Derived” will have a problem since it
gets multiple copies of class “Base” from both of the intermediate classes “Derived1” and “Derived2”.
This multiplicity or duplication of code in the final derived class is known as Death of diamond or
Dreaded diamond. This can be solved by using the virtual keyword while inheriting the base class into
its derived classes and this concept is known as virtual base class concept.
So the above syntax has to be modified as
class Base
{
--------
};
class Derived1: virtual mode Base
{
--------
};
class Derived2 : virtual mode Base
{
--------
};
class Derived: mode Derived1,mode Derived2
{
-------
};

Lets see a technical implementation of hybrid inheritance using virtual concept. We will concentrate on
the same student’s example here.

© Cranes Varsity V2.1-2020-21 86/ 126


C++ www.cranesvarsity.com

Engineering_Student

#Project_Title : String
#Project_Marks : float
+Set_Project_Title(char*):void
+Set_Project_Marks(float):void

Electronics_Engg_Student Computer_Engg_Student
#Device_No : Integer
#Device_Name : String #Programming_Lang : String

+Set_Device_Name(char*):void
+Set_Device_No(int):void +Set_Language(char *):void

Embedded_Engg_Student

+Ret_Device_Name():char*
+Ret_Device_No():int
+Ret_Programming_Lang():char*

//Program to illustrate virtual base class concept


//engg_student.h – Header file of engineering student
#include <iostream>
using namespace std;

#ifndef __ENGG_STUDENT
#define __ENGG_STUDENT

class Engineering_Student
{
protected:
char Project_Title[20];
float Project_Marks;
public:
void Set_Project_Title(char *);
void Set_Project_Marks(float);
};
#endif

© Cranes Varsity V2.1-2020-21 87/ 126


C++ www.cranesvarsity.com

//engg_student_lib.cpp – Library layer of engineering student


#include "engg_student.h"

void Engineering_Student :: Set_Project_Title(char *t)


{
strcpy(Project_Title, t);
}
void Engineering_Student :: Set_Project_Marks(float p)
{
Project_Marks = p;
}

//comp_engg.h – Header file of computer engineer


#include <iostream>
#include "engg_student.h"
using namespace std;

#ifndef __COMP_H
#define __COMP_H

class Computer_Engg_Student : virtual public Engineering_Student


{
protected:
char Programming_Lang[20];
public:
void Set_Language(char*);
};
#endif

//comp_engg_lib.cpp – Library layer of computer engineer


#include "comp_engg.h"

void Computer_Engg_Student :: Set_Language(char *p)


{
strcpy(Programming_Lang, p);
}
//elect_engg.h – Header file of electronics engineer
#include <iostream>
#include "engg_student.h"
using namespace std;

#ifndef __ELECT_H
#define __ELECT_H

class Electronics_Engg_Student : virtual Engineering_Student


{
protected:
char Device_Name[20];
int Device_No;
public:
void Set_Device_Name(char*);
void Set_Device_No(int);
};
#endif

© Cranes Varsity V2.1-2020-21 88/ 126


C++ www.cranesvarsity.com

//elect_engg_lib.cpp
#include "elect_engg.h"

void Electronics_Engg_Student :: Set_Device_Name(char *n)


{
strcpy(Device_Name, n);
}

void Electronics_Engg_Student :: Set_Device_No(int n)


{
Device_No = n;
}

//embb_engg.h – Header file of embedded engineer


#include <iostream>
#include "elect_engg.h"
#include "comp_engg.h"
using namespace std;

#ifndef __EMBB_H
#define __EMBB_H

class Embedded_Engg_Student : public Electronics_Engg_Student, public


Computer_Engg_Student
{
public:
char* Ret_Project_Title();
float Ret_Project_Marks();
char* Ret_Device_Name();
int Ret_Device_No();
char* Ret_Programming_Lang();
};
#endif

//embb_engg_lib.cpp – Library layer of embedded engineer


#include "embb_engg.h"

char* Embedded_Engg_Student :: Ret_Project_Title()


{
return Project_Title;
}
float Embedded_Engg_Student :: Ret_Project_Marks()
{
return Project_Marks;
}
char* Embedded_Engg_Student :: Ret_Device_Name()
{
return Device_Name;
}
int Embedded_Engg_Student :: Ret_Device_No()
{
return Device_No;
}

© Cranes Varsity V2.1-2020-21 89/ 126


C++ www.cranesvarsity.com

char* Embedded_Engg_Student :: Ret_Programming_Lang()


{
return Programming_Lang;
}
//emb_app.cpp – Application layer
#include "embb_engg.h"

int main()
{
Embedded_Engg_Student Anu;

char dev_name[20], prog_lang[20], proj_title[20];


int dev_no;
float proj_marks;

cout << "Enter project title" << endl;


cin >> proj_title;

Anu.Set_Project_Title(proj_title);

cout << "Enter project marks" << endl;


cin >> proj_marks;

Anu.Set_Project_Marks(proj_marks);

cout << "Enter the device name" << endl;


cin >> dev_name;

Anu.Set_Device_Name(dev_name);

cout << "Enter the device no" << endl;


cin >> dev_no;

Anu.Set_Device_No(dev_no);

cout << "Enter the programming language" << endl;


cin >> prog_lang;

Anu.Set_Language(prog_lang);

cout << "----Displaying the details----" << endl;


cout << “Project name is = ” << Anu.Ret_Project_Title() << endl;
cout << “Project marks is = ” << Anu.Ret_Project_Marks() << endl;
cout << "Device name is = " << Anu.Ret_Device_Name() << endl;
cout << "Device no is = " << Anu.Ret_Device_No() << endl;
cout << "Programming language is = " << Anu.Ret_Programming_Lang() << endl;

return 0;
}

© Cranes Varsity V2.1-2020-21 90/ 126


C++ www.cranesvarsity.com

Output :

Enter project title


Cryptography
Enter project marks
54.5
Enter the device name
PCB
Enter the device no
1020
Enter the programming language
JAVA
----Displaying the details----
Project name is = Cryptography
Project marks is = 54.5
Device name is = PCB
Device no is = 1020
Programming language is = JAVA

Constructors and destructors in inheritance

The behavior of constructors and destructors in inheritance is an important concept for discussion.
There are three types of member functions of a class that can be accessed but never inherited. Some
member functions can only be accessed through the derived class objects and not inherited. These
are
 Constructors
 Overloaded assignment operator
There are certain important concepts to make note of when working with constructors and destructors
in inheritance.
 Order of construction: The base class constructors are always executed first followed
by the derived class constructors
 Order of destruction: The derived class destructor is always executed before the base
class destructor
 If the parameterized constructor is present in the base class, then the immediate derived
class constructor has to take care of supplying the arguments to the constructor of the
base class
 By default, all the derived class constructors invoke base class default constructors
 To invoke any other constructor, make use of the derived constructor’s initializer list.
//Program illustrating the call of constructors and destructors: Multiple inheritance
//comp_engg.h – Header file of computer engineering student
#include <iostream>
using namespace std;
#ifndef __COMP_H
#define __COMP_H
class Computer_Engg_Student
{
protected:
char Programming_Lang[20];
public:
Computer_Engg_Student();
Computer_Engg_Student(char*);
};
#endif

© Cranes Varsity V2.1-2020-21 91/ 126


C++ www.cranesvarsity.com

//comp_engg_lib.cpp – Library layer of computer engineering student


#include "comp_engg.h"
Computer_Engg_Student :: Computer_Engg_Student()
{
strcpy(Programming_Lang, "");
}
Computer_Engg_Student :: Computer_Engg_Student(char *p)
{
strcpy(Programming_Lang, p);
}
//elect_engg.h – Header file of electronics engineering student
#include <iostream>
using namespace std;
#ifndef __ELECT_H
#define __ELECT_H
class Electronics_Engg_Student
{
protected:
char Device_Name[20];
int Device_No;
public:
Electronics_Engg_Student();
Electronics_Engg_Student(char*, int);
};
#endif
//elect_engg_lib.cpp – Library layer of electronics engineering student
#include "elect_engg.h"
Electronics_Engg_Student :: Electronics_Engg_Student()
{
strcpy(Device_Name, " ");
Device_No = 0;
}
Electronics_Engg_Student :: Electronics_Engg_Student(char *n, int d)
{
strcpy(Device_Name, n);
Device_No = d;
}
//embb_engg.h – Header file of embedded engineer
#include <iostream>
#include "elect_engg.h"
#include "comp_engg.h"
using namespace std;
#ifndef __EMBB_H
#define __EMBB_H
class Embedded_Engg_Student : public Electronics_Engg_Student, public Computer_Engg_Student
{
public:
Embedded_Engg_Student();
Embedded_Engg_Student(char*, char*, int);
char* Ret_Device_Name();
int Ret_Device_No();
char* Ret_Programming_Lang();
};
#endif

© Cranes Varsity V2.1-2020-21 92/ 126


C++ www.cranesvarsity.com

//embb_engg_lib.cpp

#include "embb_engg.h"

Embedded_Engg_Student :: Embedded_Engg_Student() {}

Embedded_Engg_Student :: Embedded_Engg_Student(char *p, char *d, int n) :


Computer_Engg_Student(p), Electronics_Engg_Student(d, n) {}

char* Embedded_Engg_Student :: Ret_Device_Name( )


{
return Device_Name;
}
int Embedded_Engg_Student :: Ret_Device_No( )
{
return Device_No;
}

char* Embedded_Engg_Student :: Ret_Programming_Lang( )


{
return Programming_Lang;
}
//emb_app.cpp – Application layer
#include "embb_engg.h"

int main( )
{
Embedded_Engg_Student Anu("Java", "PCB", 1020);

cout << "----Displaying the details----" << endl;


cout << "Device name is = " << Anu.Ret_Device_Name() << endl;
cout << "Device no is = " << Anu.Ret_Device_No() << endl;
cout << "Programming language is = " << Anu.Ret_Programming_Lang() << endl;

return 0;
}

Output :

----Displaying the details----


Device name is = PCB
Device no is = 1020
Programming language is = Java

In multiple inheritances, the order of construction is the order in which the base classes appear in the
declaration of the derived class. The order of destruction is the reverse of the order of destruction

Execution of class constructors

Type of inheritance Order of execution


class derived: public base base()
derived()

class derived: public base1, public base2 base1()


base2()
derived()
© Cranes Varsity
class derived: public base1,virtual publicV2.1-2020-21
base2() 93/ 126

base2 base1()
derived()
C++ www.cranesvarsity.com

Note : Try out constructor calls in single, multilevel and hierarchical inheritance in a similar manner.

The rule of immediate derived class passing arguments to base is valid in all the inheritance except
hybrid / diamond inheritance. In hybrid inheritance, since Derived1 and Derived2 are immediate
derived classes of base, passing arguments to base through both these classes will result in re-
initialization of the
//comp_engg.h single base
– Header location.
file of computerSo in hybrid inheritance, the indirect derived class, ie) Derived
engineer
will have to take
#include <iostream> up the responsibility of passing arguments to base.
#include "engg_student.h"
Modifying
using the hybrid
namespace std;inheritance example with constructors

#ifndef __COMP_H
//Program illustrating parameter passing in hybrid inheritance
#define __COMP_H
//engg_student.h – Header file of engineering student
#include <iostream>
class
using Computer_Engg_Student
namespace std; : virtual public Engineering_Student
{ __ENGG_STUDENT
#ifndef
protected:
#define __ENGG_STUDENT
char Programming_Lang[20];
class Engineering_Student
{ public:
Computer_Engg_Student();
protected:
Computer_Engg_Student(char*,
char Project_Title[20]; float, char*);
}; float Project_Marks;
#endif public:
//comp_engg_lib.cpp – Library layer of computer engineer
Engineering_Student();
#include "comp_engg.h"
Engineering_Student(char*, float);
};
Computer_Engg_Student
#endif :: Computer_Engg_Student()
{ //engg_student_lib.cpp – Library layer of engineering student
strcpy(Programming_Lang,
#include "engg_student.h" "");
} Engineering_Student :: Engineering_Student()
{
Computer_Engg_Student
strcpy(Project_Title, ""); :: Computer_Engg_Student(char *p, float m, char *l) :
Engineering_Student(p,m)
Project_Marks = 0;
{}
strcpy(Programming_Lang,
Engineering_Student l);
:: Engineering_Student(char *p, float m)
}{
//elect_engg.h – Header file
strcpy(Project_Title, p);of electronics engineer
#include <iostream> = m;
Project_Marks
#include
} "engg_student.h"
using namespace std;

#ifndef __ELECT_H
#define __ELECT_H

class Electronics_Engg_Student : virtual Engineering_Student


{
protected:
char Device_Name[20];
int Device_No;
public:
Electronics_Engg_Student();
Electronics_Engg_Student(char*, float, char*, int);
};
#endif

© Cranes Varsity V2.1-2020-21 94/ 126


C++ www.cranesvarsity.com

//elect_engg_lib.cpp – Library layer of electronics engineer


#include "elect_engg.h"

Electronics_Engg_Student::Electronics_Engg_Student()
{
strcpy(Device_Name, "");
Device_No = 0;
}

Electronics_Engg_Student::Electronics_Engg_Student(char *p,float m,char *d,int n)


:Engineering_Student(p,m)
{
strcpy(Device_Name, d);
Device_No = n;
}

//embb_engg.h – Header file of embedded engineer


#include <iostream>
#include "elect_engg.h"
#include "comp_engg.h"
using namespace std;

#ifndef __EMBB_H
#define __EMBB_H

class Embedded_Engg_Student : public Electronics_Engg_Student, public


Computer_Engg_Student
{
public:
Embedded_Engg_Student();
Embedded_Engg_Student(char*, float, char*, int, char*);
char* Ret_Project_Title();
float Ret_Project_Marks();
char* Ret_Device_Name();
int Ret_Device_No();
char* Ret_Programming_Lang();
};
#endif
//embb_engg_lib.cpp – Library layer of embedded engineer
#include "embb_engg.h"

Embedded_Engg_Student :: Embedded_Engg_Student() {}

Embedded_Engg_Student :: Embedded_Engg_Student(char *p, float m, char *d, int n, char *l)


: Engineering_Student(p, m),
Electronics_Engg_Student(0,0,d,n),
Computer_Engg_Student(0,0,l) {}
char* Embedded_Engg_Student :: Ret_Project_Title()
{
return Project_Title;
}
© Cranes Varsity V2.1-2020-21 95/ 126
C++ www.cranesvarsity.com

float Embedded_Engg_Student :: Ret_Project_Marks()


{
return Project_Marks;
}
char* Embedded_Engg_Student :: Ret_Device_Name()
{
return Device_Name;
}
int Embedded_Engg_Student :: Ret_Device_No()
{
return Device_No;
}
char* Embedded_Engg_Student :: Ret_Programming_Lang()
{
return Programming_Lang;
}
//emb_app.cpp – Application layer
#include "embb_engg.h"
int main()
{
Embedded_Engg_Student Anu("Cryptography", 34.5, "PCB", 1020, "JAVA");
cout << "----Displaying the details----" << endl;
cout << "Project name is = " << Anu.Ret_Project_Title() << endl;
cout << "Project marks is = " << Anu.Ret_Project_Marks() << endl;
cout << "Device name is = " << Anu.Ret_Device_Name() << endl;
cout << "Device no is = " << Anu.Ret_Device_No() << endl;
cout << "Programming language is = " << Anu.Ret_Programming_Lang() << endl;
return 0;
}

Output:

----Displaying the details----


Project name is = Cryptography
Project marks is = 34.5
Device name is = PCB
Device no is = 1020
Programming language is = JAVA

NOTE: Try out what will happen if you are not passing the arguments for base through final derived
class.
Few points to be noted:

 While working with inheritance, use upward facing arrow, since the base class cannot look into
the derived class where as the derived class knows everything about the base class.
 If the mode of inheritance is not specified, the default mode is private for class.
 The size of the derived class is equal to the sum of the size of base class data members
plus size of derived class data members
 The difference between the private and the protected modes of inheritance is that once
inherited through the private mode, further access of the base class members through the
derived class becomes impossible

© Cranes Varsity V2.1-2020-21 96/ 126


C++ www.cranesvarsity.com

CHAPTER - 9
RUN TIME POLYMORPHISM

Polymorphism refers to “Single Interface and Multiple Implementations”. As we have already


discussed, operator overloading and function overloading are types of compile time polymorphism
since the binding is taking place during compile time. We can even have run time polymorphism where
in binding is taking place during run time.

In this chapter, let’s see, what is the purpose of going for run time polymorphism and how to
implement run time polymorphism in C++.

Purpose of going for run time polymorphism

In inheritance, a pointer created for base class can point either to base class objects or any of its
derived class objects.

Base class pointer pointing to derived class objects is technically termed as “upcasting” and
it is perfectly legal in C++.

Consider the following hierarchy :

Let’s say Base


Base_Obj is an object of Base and Base_Ptr is a pointer to base object
Der1_Obj is an object to Derived1 and Der1_Ptr is a pointer to Derived1 object
Der2_Obj is an object to Derived2 and Der1_Ptr is a pointer to Derived2 object
Derived1
Then
Base_Ptr = &Base_Obj;
Base_Ptr = &Der1_Obj;
Base_Ptr = &Der2_Obj are perfectly legal.
Derived2

Where as the reverse

Derived class pointer pointing to base class objects is illegal and is technically termed as
“downcasting”.

Another situation where we can illustrate upcasting


Base_Obj = Der1_Obj;
This is a perfectly legal operation. In this case, the added data members in the derived class will be
thrown away and the base portion of the derived object will be assigned to base object. This is known
as object slicing.

Note: Try to check if Der1_Obj = Base_Obj is a valid operation ?

© Cranes Varsity V2.1-2020-21 97/ 126


C++ www.cranesvarsity.com

In upcasting, base class pointers can access only


 Base members inherited to derived
 Overridden member functions

While establishing “is a relationship”, there is a facility of writing functions with the same
name, arguments and return type in the base class and the derived class. This is known as
function overriding.

Let’s analyze the behaviour of a program with the above mentioned concepts.

Student

-Name: String
#Attendence: Float
#Internal_Marks: Float
#External_Marks: Float
#Total_Marks: Float
+ Student ()
+Student (char*, float, float, float)
+Calculate_Marks(): void
+Calculate_Percentage(int)

Engineering_Student Medical_Student

+Engineering_Student() +Medical_Student()
+Engineering_Student(char*, float, float, float) +Medical_Student(char*, float, float, float)
+Calculate_Marks(): void

© Cranes Varsity V2.1-2020-21 98/ 126


C++ www.cranesvarsity.com

//Program to illustrate function overriding and pointers


//student.h – Header file of student
#include <iostream>
using namespace std;
#ifndef __STUDENT_H
#define __STUDENT_H
class Student
{
private:
char Name[20];
protected:
float Attendence;
float Internal_Marks;
float External_Marks;
float Total_Marks;
public:
Student();
Student(char*, float, float, float);
void Calculate_Marks();
float Calculate_Percentage(int);

};
#endif

//student_lib.cpp – Library layer of student


#include "student.h"
Student :: Student()
{
strcpy(Name, "");
Attendence = 0.0;
Internal_Marks = 0.0;
External_Marks = 0.0;
Total_Marks = 0;
}
Student :: Student(char *n, float a, float i,float e)
{
strcpy(Name, n);
Attendence = a;
Internal_Marks = i;
External_Marks = e;
Total_Marks = 0;
}
void Student :: Calculate_Marks()
{
Total_Marks = Internal_Marks + External_Marks;
}
float Student :: Calculate_Percentage(int no_subjects)
{
return (Total_Marks / (float)no_subjects);
}

© Cranes Varsity V2.1-2020-21 99/ 126


C++ www.cranesvarsity.com

//engg_student.h – Header file of engineering student


#include <iostream>
#include "student.h"
using namespace std;
#ifndef __ENGG_H
#define __ENGG_H

class Engineering_Student : public Student


{
public:
Engineering_Student();
Engineering_Student(char*, float, float, float);
void Calculate_Marks();
};
#endif

//engg_student_lib.cpp – Library layer of engineering student


#include "engg_student.h"
Engineering_Student :: Engineering_Student() {}

Engineering_Student :: Engineering_Student(char *n, float a, float i, float e)


: Student(n, a, i, e) {}
void Engineering_Student :: Calculate_Marks()
{
int Attendence_Marks;

if(Attendence > 90)


Attendence_Marks = 5;
else if(Attendence > 80 && Attendence <= 90 )
Attendence_Marks = 3;
else if(Attendence >= 60 && Attendence <= 80)
Attendence_Marks = 1;
else
Attendence_Marks = 0;

Total_Marks = Internal_Marks + External_Marks + Attendence_Marks;


}

//medical_student.h – Header file of medical student


#include <iostream>
#include "student.h"
using namespace std;

#ifndef __MED_H
#define __MED_H
class Medical_Student : public Student
{
public:
Medical_Student();
Medical_Student(char*, float, float, float);
};
#endif

© Cranes Varsity V2.1-2020-21 100/ 126


C++ www.cranesvarsity.com

//medical_student_lib.cpp – Library layer of medical student


#include "medical_student.h"

Medical_Student :: Medical_Student() {}
Medical_Student :: Medical_Student(char *n, float a, float i, float e)
: Student(n, a, i, e) {}
//med_engg_app.cpp – Application layer
#include "student.h"
#include "engg_student.h"
#include "medical_student.h"

int main()
{
Student *Sptr, Anu("Anu", 80, 200, 200);
Engineering_Student Vinu("Vinu", 94, 400, 400);
Medical_Student Sonu("Sonu", 55, 100, 100);
int no_subjects = 10;
Sptr = &Anu;
Sptr -> Calculate_Marks();
cout << "Anu's marks = ";
cout << Sptr -> Calculate_Percentage(no_subjects) << endl;

The actual output


Sptrexpected
= &Vinu; for the above program is
Sptr -> Calculate_Marks();
cout << "Vinu's marks = ";
cout << Sptr -> Calculate_Percentage(no_subjects) << endl;

Sptr = &Sonu;
Sptr -> Calculate_Marks();
cout << "Sonu's marks = ";
cout << Sptr -> Calculate_Percentage(no_subjects) << endl;

return 0;
}

Actual Output Expected:


Anu's marks = 40
Vinu's marks = 80.5
Sonu's marks = 20

But when you compile the program the program and see the behaviour, the output you will obtain is :

Output :
Anu's marks = 40
Vinu's marks = 80
Sonu's marks = 20

© Cranes Varsity V2.1-2020-21 101/ 126


C++ www.cranesvarsity.com

As you can notice we are not getting the expected output while calculating vinu (derived class object)
This variation in the output is a result of compile time binding or compile time polymorphism. Here
compiler is just checking the type of the pointer (which is always student*) and doing the binding
according to the type. So instead of calling the overridden functions in the ‘Engineering_Student’ class,
always the function inherited from ‘Student class’ will get invoked. It is in this type of situations, the
real need of run time polymorphism arises.

Implementations of Run time polymorphism

Run time binding or late binding is achieved by using the virtual keyword when declaring the functions
in the base class. To create a virtual function, precede the declaration of the function with the keyword
virtual. Note, only the declaration need the virtual keyword, not the definition.

Lets try to modify the above program with virtual functions.

//Program to illustrate run time polymorphism


//student.h – Header file of student
#include <iostream>
using namespace std;

#ifndef __STUDENT_H
#define __STUDENT_H
class Student
{
private:
char Name[20];
protected:
float Attendence;
float Internal_Marks;
float External_Marks;
float Total_Marks;
public:
Student();
Student(char*, float, float, float);
virtual void Calculate_Marks();
float Calculate_Percentage(int);

};
#endif
//student_lib.cpp – Library layer of student
#include "student.h"

Student :: Student()
{
strcpy(Name, "");
Attendence = 0.0;
Internal_Marks = 0.0;
External_Marks = 0.0;
Total_Marks = 0;
}

© Cranes Varsity V2.1-2020-21 102/ 126


C++ www.cranesvarsity.com

Student :: Student(char *n, float a, float i,float e)


{
strcpy(Name, n);
Attendence = a;
Internal_Marks = i;
External_Marks = e;
Total_Marks = 0;
}
void Student :: Calculate_Marks()
{
Total_Marks = Internal_Marks + External_Marks;
}

float Student :: Calculate_Percentage(int no_subjects)


{
return (Total_Marks / (float)no_subjects);
}

//engg_student.h – Header file of engineering student


#include <iostream>
#include "student.h"
using namespace std;

#ifndef __ENGG_H
#define __ENGG_H

class Engineering_Student : public Student


{
public:
Engineering_Student();
Engineering_Student(char*, float, float, float);
void Calculate_Marks();
};
#endif

//engg_student_lib.cpp – Library layer of engineering student


#include "engg_student.h"
Engineering_Student :: Engineering_Student() {}
Engineering_Student :: Engineering_Student(char *n, float a, float i, float e)
: Student(n, a, i, e) {}
void Engineering_Student :: Calculate_Marks()
{
int Attendence_Marks;

if(Attendence > 90)


Attendence_Marks = 5;
else if(Attendence > 80 && Attendence <= 90 )
Attendence_Marks = 3;
else if(Attendence >= 60 && Attendence <= 80)
Attendence_Marks = 1;
else
Attendence_Marks = 0;

Total_Marks = Internal_Marks + External_Marks + Attendence_Marks;


}

© Cranes Varsity V2.1-2020-21 103/ 126


C++ www.cranesvarsity.com

//medical_student.h – Header file of medical student


#include <iostream>
#include "student.h"
using namespace std;

#ifndef __MED_H
#define __MED_H
class Medical_Student : public Student
{
public:
Medical_Student();
Medical_Student(char*, float, float, float);
};
#endif
//medical_student_lib.cpp – Library layer of medical student

#include "medical_student.h"

Medical_Student :: Medical_Student() {}

Medical_Student :: Medical_Student(char *n, float a, float i, float e)


: Student(n, a, i, e) {}
//med_engg_app.cpp – Application layer
#include "student.h"
#include "engg_student.h"
#include "medical_student.h"

int main()
{
Student *Sptr, Anu("Anu", 80, 200, 200);
Engineering_Student Vinu("Vinu", 94, 400, 400);
Medical_Student Sonu("Sonu", 55, 100, 100);

int no_subjects = 10;

Sptr = &Anu;
Sptr -> Calculate_Marks();
cout << "Anu's marks = ";
cout << Sptr -> Calculate_Percentage(no_subjects) << endl;

Sptr = &Vinu;
Sptr -> Calculate_Marks();
cout << "Vinu's marks = ";
cout << Sptr -> Calculate_Percentage(no_subjects) << endl;

Sptr = &Sonu;
Sptr -> Calculate_Marks();
cout << "Sonu's marks = "; Output :
cout << Sptr -> Calculate_Percentage(no_subjects) << endl; Anu's marks = 40
Vinu's marks = 80.5
return 0;
Sonu's marks = 20
}

© Cranes Varsity V2.1-2020-21 104/ 126


C++ www.cranesvarsity.com

If a function is made as virtual in the base class, all the overridden functions in the derived class also
becomes virtual. The keyword virtual tells the compiler to automatically install all the mechanisms
needed for late binding. As part of it, compiler creates a single table called “Virtual Table or Vtable” for
each class. Compiler places the address of the virtual functions for that particular class in the Vtable.

In each class with virtual functions, compiler places an implicit pointer called the virtual pointer (vptr)
when it compiles the virtual functions of a class. It inserts code along with the constructor to initialize
the virtual pointer.The vptr holds the address of the V-table of that class.

When a virtual function call is made through a base class pointer, the compiler quickly inserts code to
fetch the Vptr and look up the function address in the Vtable thus calling the correct function and
causing late binding to take place.

Virtual Destructors

Consider the following program :

//student.h – Header file of student


#include <iostream>
using namespace std;

#ifndef __STUDENT_H
#define __STUDENT_H
class Student
{
private:
char Name[20];
protected:
float Attendence;
float Internal_Marks;
float External_Marks;
float Total_Marks;
public:
Student();
Student(char*, float, float, float);
virtual void Calculate_Marks();
float Calculate_Percentage(int);
~Student();

};
#endif
//student_lib.cpp – Library layer of student
#include "student.h"

Student :: Student()
{
strcpy(Name, "");
Attendence = 0.0;
Internal_Marks = 0.0;
External_Marks = 0.0;
Total_Marks = 0;
}

© Cranes Varsity V2.1-2020-21 105/ 126


C++ www.cranesvarsity.com

Student :: Student(char *n, float a, float i,float e)


{
strcpy(Name, n);
Attendence = a;
Internal_Marks = i;
External_Marks = e;
Total_Marks = 0;
}
void Student :: Calculate_Marks()
{
Total_Marks = Internal_Marks + External_Marks;
}
float Student :: Calculate_Percentage(int no_subjects)
{
return (Total_Marks / (float)no_subjects);
}
Student :: ~Student() {}

//engg_student.h – Header file of engineering student


#include <iostream>
#include "student.h"
using namespace std;
#ifndef __ENGG_H
#define __ENGG_H

class Engineering_Student : public Student


{
public:
Engineering_Student();
Engineering_Student(char*, float, float, float);
void Calculate_Marks();
~Engineering_Student();
};
#endif

//engg_student_lib.cpp – Library layer of engineering student


#include "engg_student.h"
Engineering_Student :: Engineering_Student() {}
Engineering_Student :: Engineering_Student(char *n, float a, float i, float e)
: Student(n, a, i, e) {}
Engineering_Student :: ~Engineering_Student() {}
void Engineering_Student :: Calculate_Marks()
{
int Attendence_Marks;
if(Attendence > 90)
Attendence_Marks = 5;
else if(Attendence > 80 && Attendence <= 90 )
Attendence_Marks = 3;
else if(Attendence >= 60 && Attendence <= 80)
Attendence_Marks = 1;
else
Attendence_Marks = 0;
Total_Marks = Internal_Marks + External_Marks + Attendence_Marks;
}

© Cranes Varsity V2.1-2020-21 106/ 126


C++ www.cranesvarsity.com

//stud_engg_app.cpp – Application layer


#include "student.h"
#include "engg_student.h"
int main()
{
Student *Sptr;
Sptr = new Engineering_Student;
delete Sptr;
return 0;
}

When you run this program, you can see that delete Sptr calls only the base class destructor (Student
class destructor). The problem here is since Sptr is of type base, the compiler can only call the base
class destructor. So this problem introduces memory leakage in the program. Here also the solution is
going to be with virtual keyword.

Modifying the above program.

//student.h – Header file of student


#include <iostream>
using namespace std;
#ifndef __STUDENT_H
#define __STUDENT_H
class Student
{
private:
char Name[20];
protected:
float Attendence;
float Internal_Marks;
float External_Marks;
float Total_Marks;
public:
Student();
Student(char*, float, float, float);
virtual void Calculate_Marks();
float Calculate_Percentage(int);
virtual ~Student();

};
#endif
//student_lib.cpp – Library layer of student
#include "student.h"
Student :: Student()
{
strcpy(Name, "");
Attendence = 0.0;
Internal_Marks = 0.0;
External_Marks = 0.0;
Total_Marks = 0;
}

© Cranes Varsity V2.1-2020-21 107/ 126


C++ www.cranesvarsity.com

Student :: Student(char *n, float a, float i,float e)


{
strcpy(Name, n);
Attendence = a;
Internal_Marks = i;
External_Marks = e;
Total_Marks = 0;
}
void Student :: Calculate_Marks()
{
Total_Marks = Internal_Marks + External_Marks;
}

float Student :: Calculate_Percentage(int no_subjects)


{
return (Total_Marks / (float)no_subjects);
}
Student :: ~Student() {}

//engg_student.h – Header file of engineering student


#include <iostream>
#include "student.h"
using namespace std;
#ifndef __ENGG_H
#define __ENGG_H

class Engineering_Student : public Student


{
public:
Engineering_Student();
Engineering_Student(char*, float, float, float);
void Calculate_Marks();
~Engineering_Student();
};
#endif
//engg_student_lib.cpp – Library layer of engineering student
#include "engg_student.h"
Engineering_Student :: Engineering_Student() {}
Engineering_Student :: Engineering_Student(char *n, float a, float i, float e)
: Student(n, a, i, e) {}
void Engineering_Student :: Calculate_Marks()
{
int Attendence_Marks;
if(Attendence > 90)
Attendence_Marks = 5;
else if(Attendence > 80 && Attendence <= 90 )
Attendence_Marks = 3;
else if(Attendence >= 60 && Attendence <= 80)
Attendence_Marks = 1;
else
Attendence_Marks = 0;
Total_Marks = Internal_Marks + External_Marks + Attendence_Marks;
}
Engineering_Student :: ~Engineering_Student() {}

© Cranes Varsity V2.1-2020-21 108/ 126


C++ www.cranesvarsity.com

//stud_engg_app.cpp – Application layer

#include "student.h"
#include "engg_student.h"

int main()
{
Student *Sptr;
Sptr = new Engineering_Student;
delete Sptr;
return 0;
}

Once the base class destructor is made as virtual, deleting Sptr automatically calls
Engineering_Student’s destructor as well as Student’s destructor.

Note: Even though the destructor, like the constructor is an exceptional function, it is possible to make
destructor as virtual. But constructors can never me made as virtual. This is because, during
construction, the object already knows what type it is where as during destruction it doesn’t know that.

Abstract Class
Sometimes in our design, we may want our base class to act only as an Interface class. Or in other
workds we may not want to create an object of base class but we may still want to inherit it. This is
accomplished by using the concept of “abstract classes”.
Abstract classes are classes form which we can never create objects.A class can be made abstract by
declaring at least one pure virtual function inside it. A pure virtual function is one which is equated to
zero.
Syntax :
virtual return_type function_name( )=0;

When we write a pure virtual function, compiler reserves a slot for the function in the Vtable but no
address will be put in the slot. This makes the Vtable incomplete which further makes the class
definition as incomplete. This prevents the creation of object for such a class
When an abstract class is inherited, all the pure virtual function should be overridden in all the derived
classes. Else, the derived class will also become abstract.
Lets modify the “Student” program with the concept of abstract classes.

//Program to illustrate the concept of abstract classes


//student.h – Header file of student
#include <iostream>
using namespace std;

#ifndef __STUDENT_H
#define __STUDENT_H
class Student
{
private:
char Name[20];
protected:
float Attendence;
float Internal_Marks;
float External_Marks;
float Total_Marks;

© Cranes Varsity V2.1-2020-21 109/ 126


C++ www.cranesvarsity.com

public:
Student();
Student(char*, float, float, float);
virtual void Calculate_Marks() = 0;
float Calculate_Percentage(int);

};
#endif

//student_lib.cpp – Library layer of student


#include "student.h"

Student :: Student()
{
strcpy(Name, "");
Attendence = 0.0;
Internal_Marks = 0.0;
External_Marks = 0.0;
Total_Marks = 0;
}

Student :: Student(char *n, float a, float i,float e)


{
strcpy(Name, n);
Attendence = a;
Internal_Marks = i;
External_Marks = e;
Total_Marks = 0;
}
float Student :: Calculate_Percentage(int no_subjects)
{
return (Total_Marks / (float)no_subjects);
}
//engg_student.h – Header file of engineering student
#include <iostream>
#include "student.h"
using namespace std;

#ifndef __ENGG_H
#define __ENGG_H

class Engineering_Student : public Student


{
public:
Engineering_Student();
Engineering_Student(char*, float, float, float);
void Calculate_Marks();
};
#endif

© Cranes Varsity V2.1-2020-21 110/ 126


C++ www.cranesvarsity.com

//engg_student_lib.cpp – Library layer of engineering student


#include "engg_student.h"

Engineering_Student :: Engineering_Student() {}

Engineering_Student :: Engineering_Student(char *n, float a, float i, float e)


: Student(n, a, i, e) {}
void Engineering_Student :: Calculate_Marks()
{
int Attendence_Marks;

if(Attendence > 90)


Attendence_Marks = 5;
else if(Attendence > 80 && Attendence <= 90 )
Attendence_Marks = 3;
else if(Attendence >= 60 && Attendence <= 80)
Attendence_Marks = 1;
else
Attendence_Marks = 0;
Total_Marks = Internal_Marks + External_Marks + Attendence_Marks;
}

//medical_student.h
#include <iostream>
#include "student.h"
using namespace std;

#ifndef __MED_H
#define __MED_H
class Medical_Student : public Student
{
public:
Medical_Student();
Medical_Student(char*, float, float, float);
void Calculate_Marks();
};
#endif

//medical_student_lib.cpp – Library layer of medical student


#include "medical_student.h"

Medical_Student :: Medical_Student() {}

Medical_Student :: Medical_Student(char *n, float a, float i, float e)


: Student(n, a, i, e) {}

void Medical_Student :: Calculate_Marks()


{
Total_Marks = Internal_Marks + External_Marks;
}

© Cranes Varsity V2.1-2020-21 111/ 126


C++ www.cranesvarsity.com

//med_engg_app.cpp – Application layer


#include "student.h"
#include "engg_student.h"
#include "medical_student.h"
int main()

{
Student *Sptr;
Engineering_Student Vinu("Vinu", 94, 400, 400);
Medical_Student Sonu("Sonu", 55, 100, 100);

int no_subjects = 10;

Sptr = &Vinu;
Sptr -> Calculate_Marks();
cout << "Vinu's marks = ";
cout << Sptr -> Calculate_Percentage(no_subjects) << endl;

Sptr = &Sonu;
Sptr -> Calculate_Marks();
cout << "Sonu's marks = ";
cout << Sptr -> Calculate_Percentage(no_subjects) << endl;

return 0;
}

Output :
Vinu's marks = 80.5
Sonu's marks = 20

© Cranes Varsity V2.1-2020-21 112/ 126


C++ www.cranesvarsity.com

CHAPTER - 10
EXCEPTION HANDLING

An exception is a problem that arises during the execution of a program. A C++ exception is a
response to an exceptional circumstance that arises while a program is running, such as an attempt to
divide by zero.
Exceptions provide a way to transfer control from one part of a program to another. C++ exception
handling is built upon three keywords: try, catch, and throw.
throw − A program throws an exception when a problem shows up. This is done using a throw
keyword.
catch − A program catches an exception with an exception handler at the place in a program where
you want to handle the problem. The catch keyword indicates the catching of an exception.
try − A try block identifies a block of code for which particular exceptions will be activated. It's followed
by one or more catch blocks.
Assuming a block will raise an exception, a method catches an exception using a combination of the
try and catch keywords. A try/catch block is placed around the code that might generate an exception.
Code within a try/catch block is referred to as protected code, and the syntax for using try/catch as
follows −
try {
// protected code
} catch( ExceptionName e1 ) {
// catch block
} catch( ExceptionName e2 ) {
// catch block
} catch( ExceptionName eN ) {
// catch block
}
You can list down multiple catch statements to catch different type of exceptions in case your try block
raises more than one exception in different situations.

Throwing Exceptions
Exceptions can be thrown anywhere within a code block using throw statement. The operand of the
throw statement determines a type for the exception and can be any expression and the type of the
result of the expression determines the type of exception thrown.

Following is an example of throwing an exception when dividing by zero condition occurs −

double division(int a, int b)


{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a/b);
}

Catching Exceptions
The catch block following the try block catches any exception. You can specify what type of exception
you want to catch and this is determined by the exception declaration that appears in parentheses
following the keyword catch.

© Cranes Varsity V2.1-2020-21 113/ 126


C++ www.cranesvarsity.com

try
{
// protected code
}
catch( ExceptionName e )
{
// code to handle ExceptionName exception
}

Above code will catch an exception of Exception Name type. If you want to specify that a catch block
should handle any type of exception that is thrown in a try block, you must put an ellipsis, ..., between
the parentheses enclosing the exception declaration as follows −

Try
{
// protected code
}
catch(...)
{
// code to handle any exception
}

The following is an example, which throws a division by zero exception and we catch it in catch block.

#include <iostream>
using namespace std;
double division(int a, int b)
{
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a/b);
}
int main ()
{
int x = 50;
int y = 0;
double z = 0;
Try
{
z = division(x, y);
cout << z << endl;
}
catch (const char* msg)
{
cerr << msg << endl;
}
return 0;
}

Because we are raising an exception of type const char*, so while catching this exception, we have to
use const char* in catch block. If we compile and run above code, this would produce the following
result

−Division by zero condition!

© Cranes Varsity V2.1-2020-21 114/ 126


C++ www.cranesvarsity.com

Exception Handling in C++

1) Following is a simple example to show exception handling in C++. The output of program explains
flow of execution of try/catch blocks.

#include <iostream>
Using namespace std;

Int main()
{
intx = -1;
// Some code
cout <<"Before try \n";
Try
{
cout <<"Inside try \n";
if(x <0)
{
throwx;
cout <<"After throw (Never executed) \n";
}
}
catch(intx )
{
cout <<"Exception Caught \n";
}
cout <<"After catch (Will be executed) \n";
return0;
}
Output:

Before try
Inside try
Exception Caught
After catch (Will be executed)

2) 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>
usingnamespacestd;

intmain()
{
try {
throw10;
}
catch(char*excp) {
cout <<"Caught "<<excp;
}
catch(...) {
cout <<"Default Exception\n";
}
return 0;
}
Output:

Default Exception

© Cranes Varsity V2.1-2020-21 115/ 126


C++ www.cranesvarsity.com

3) 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(intx) {

cout <<"Caught "<<x;

catch(...) {

cout <<"Default Exception\n";

return0;

Output:

Default Exception

4) 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>
usingnamespacestd;

Int main()
{
try {
throw'a';
}
catch(intx) {
cout <<"Caught ";
}
return0;
}

© Cranes Varsity V2.1-2020-21 116/ 126


C++ www.cranesvarsity.com

Output:

terminate called after throwing an instance of 'char'.


We can change this abnormal termination behavior by writing our own unexpected function.

5) A derived class exception should be caught before a base class exception.

6) Like Java, C++ library has a standard exception class which is base class for all standard
exceptions. All objects thrown by components of the standard library are derived from this class.
Therefore, all standard exceptions can be caught by catching this type

7) Unlike Java, in C++, all exceptions are unchecked. Compiler doesn’t check whether an exception is
caught or not . 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.

filter_none

edit

play_arrow

brightness_4

#include <iostream>
usingnamespacestd;

// This function signature is fine by the compiler, but not recommended.


// Ideally, the function should specify all uncaught exceptions and function
// signature should be "void fun(int *ptr, int x) throw (int *, int)"
voidfun(int*ptr, intx)
{
if(ptr == NULL)
throwptr;
if(x == 0)
throwx;
/* Some functionality */
}

int main()
{
try{
fun(NULL, 0);
}
catch(...) {
cout <<"Caught exception from fun()";
}
return0;
}
Output:

Caught exception from fun()

8) In C++, try-catch blocks can be nested. Also, an exception can be re-thrown using “throw; ”

#include <iostream>
usingnamespacestd;

intmain()
{
try{

© Cranes Varsity V2.1-2020-21 117/ 126


C++ www.cranesvarsity.com

try {
throw20;
}
catch(intn) {
cout <<"Handle Partially ";
throw; //Re-throwing an exception
}
}
catch(intn) {
cout <<"Handle remaining ";
}
return0;
}
Output:

Handle Partially Handle remaining

A function can also re-throw a function using same “throw; “. A function can handle a part and can ask
the caller to handle remaining.

9) 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>
usingnamespacestd;

classTest {
public:
Test() { cout <<"Constructor of Test "<<endl; }
~Test() { cout <<"Destructor of Test " <<endl; }
};

intmain() {
try{
Test t1;
throw10;
} catch(inti) {
cout <<"Caught "<<i <<endl;
}
}
Output:

Constructor of Test
Destructor of Test
Caught 10

C++ Standard Exceptions


C++ provides a list of standard exceptions defined in <exception> which we can use in our programs.
These are arranged in a parent-child class hierarchy shown below −

Here is the small description of each exception mentioned in the above hierarchy −

© Cranes Varsity V2.1-2020-21 118/ 126


C++ www.cranesvarsity.com

Sr.No Exception &Description


std::exception
1
An exception and parent class of all the standard C++ exceptions.

std::bad_alloc
2
This can be thrown by new.

std::bad_cast
3
This can be thrown by dynamic_cast.

std::bad_exception
4
This is useful device to handle unexpected exceptions in a C++ program.

std::bad_typeid
5
This can be thrown by typeid.

std::logic_error
6
An exception that theoretically can be detected by reading the code.

std::domain_error
7
This is an exception thrown when a mathematically invalid domain is used.

std::invalid_argument
8
This is thrown due to invalid arguments.

std::length_error
9
This is thrown when a too big std::string is created.

std::out_of_range

10 This can be thrown by the 'at'method, for example a std::vector and std::bitset<>::operator[]
().

std::runtime_error
11
An exception that theoretically cannot be detected by reading the code.

12 std::overflow_error

© Cranes Varsity V2.1-2020-21 119/ 126


C++ www.cranesvarsity.com

This is thrown if a mathematical overflow occurs.

std::range_error
13
This is occurred when you try to store a value which is out of range.

std::underflow_error
14
This is thrown if a mathematical underflow occurs.

Define New Exceptions


You can define your own exceptions by inheriting and overriding exception class functionality.
Following is the example, which shows how you can use std::exception class to implement your own
exception in standard way −

#include <iostream>
#include <exception>
using namespace std;

struct MyException : public exception {


const char * what () const throw () {
return "C++ Exception";
}
};

int main() {
try {
throw MyException();
} catch(MyException& e) {
std::cout << "MyException caught" << std::endl;
std::cout << e.what() << std::endl;
} catch(std::exception& e) {
//Other errors
}
}

This would produce the following result −

MyException caught
C++ Exception

Here, what() is a public method provided by exception class and it has been overridden by all the child
exception classes. This returns the cause of an exception.

© Cranes Varsity V2.1-2020-21 120/ 126


C++ www.cranesvarsity.com

CHAPTER - 11
MULTITHREADING

Multithreading is a specialized form of multitasking and a multitasking is the feature that allows your
computer to run two or more programs concurrently. In general, there are two types of multitasking:
process-based and thread-based.

Process-based multitasking handles the concurrent execution of programs. Thread-based multitasking


deals with the concurrent execution of pieces of the same program.

A multithreaded program contains two or more parts that can run concurrently. Each part of such a
program is called a thread, and each thread defines a separate path of execution.

C++ does not contain any built-in support for multithreaded applications. Instead, it relies entirely upon
the operating system to provide this feature.

Creating Threads

The following routine is used to create a POSIX thread −

#include <pthread.h>
pthread_create (thread, attr, start_routine, arg)
Here, pthread_create creates a new thread and makes it executable. This routine can be called any
number of times from anywhere within your code. Here is the description of the parameters −

Sr.No Parameter &Description


thread
1
An opaque, unique identifier for the new thread returned by the subroutine.
attr
2 An opaque attribute object that may be used to set thread attributes. You can specify a thread
attributes object, or NULL for the default values.
start_routine
3
The C++ routine that the thread will execute once it is created.
arg
4 A single argument that may be passed to start_routine. It must be passed by reference as a pointer
cast of type void. NULL may be used if no argument is to be passed.

The maximum number of threads that may be created by a process is implementation dependent.
Once created, threads are peers, and may create other threads. There is no implied hierarchy or
dependency between threads.

Terminating Threads

There is following routine which we use to terminate a POSIX thread −


#include <pthread.h>
pthread_exit (status)

Here pthread_exit is used to explicitly exit a thread. Typically, the pthread_exit() routine is called after
a thread has completed its work and is no longer required to exist.
If main() finishes before the threads it has created, and exits with pthread_exit(), the other threads will
continue to execute. Otherwise, they will be automatically terminated when main() finishes.

© Cranes Varsity V2.1-2020-21 121/ 126


C++ www.cranesvarsity.com

Example

This simple example code creates 5 threads with the pthread_create() routine. Each thread prints a
"Hello World!" message, and then terminates with a call to pthread_exit().
#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
void *PrintHello(void *threadid) {
long tid;
tid = (long)threadid;
cout << "Hello World! Thread ID, " << tid << endl;
pthread_exit(NULL);
}
int main () {
pthread_t threads[NUM_THREADS];
int rc;
int i;

for( i = 0; i < NUM_THREADS; i++ ) {


cout << "main() : creating thread, " << i << endl;
rc = pthread_create(&threads[i], NULL, PrintHello, (void *)i);

if (rc) {
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
}

Compile the following program using -lpthread library as follows −


$gcc test.cpp -lpthread

Now, execute your program which gives the following output −

main() : creating thread, 0


main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Hello World! Thread ID, 0
Hello World! Thread ID, 1
Hello World! Thread ID, 2
Hello World! Thread ID, 3
Hello World! Thread ID, 4

© Cranes Varsity V2.1-2020-21 122/ 126


C++ www.cranesvarsity.com

Passing Arguments to Threads-


This example shows how to pass multiple arguments via a structure.
You can pass any data type in a thread callback because it points to void as explained in the following
example −

#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
struct thread_data {
int thread_id;
char *message;
};

void *PrintHello(void *threadarg) {


struct thread_data *my_data;
my_data = (struct thread_data *) threadarg;
cout << "Thread ID : " << my_data->thread_id ;
cout << " Message : " << my_data->message << endl;

pthread_exit(NULL);
}
int main () {
pthread_t threads[NUM_THREADS];
struct thread_data td[NUM_THREADS];
int rc;
int i;

for( i = 0; i < NUM_THREADS; i++ ) {


cout <<"main() : creating thread, " << i << endl;
td[i].thread_id = i;
td[i].message = "This is message";
rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&td[i]);

if (rc) {
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL);
}
When the above code is compiled and executed, it produces the following result −
main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4

© Cranes Varsity V2.1-2020-21 123/ 126


C++ www.cranesvarsity.com

Thread ID : 3 Message : This is message


Thread ID : 2 Message : This is message
Thread ID : 0 Message : This is message
Thread ID : 1 Message : This is message
Thread ID : 4 Message : This is message

Joining and Detaching Threads

There are following two routines which we can use to join or detach threads −
pthread_join (threadid, status)
pthread_detach (threadid)

The pthread_join() subroutine blocks the calling thread until the specified 'threadid' thread terminates.
When a thread is created, one of its attributes defines whether it is joinable or detached. Only threads
that are created as joinable can be joined. If a thread is created as detached, it can never be joined.

This example demonstrates how to wait for thread completions by using the Pthread join routine.
#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
using namespace std;
#define NUM_THREADS 5
void *wait(void *t) {
int i;
long tid;
tid = (long)t;
sleep(1);
cout << "Sleeping in thread " << endl;
cout << "Thread with id : " << tid << " ...exiting " << endl;
pthread_exit(NULL);
}

int main () {
int rc;
int i;
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
void *status;

// Initialize and set thread joinable


pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

for( i = 0; i < NUM_THREADS; i++ ) {


cout << "main() : creating thread, " << i << endl;
rc = pthread_create(&threads[i], &attr, wait, (void *)i );
if (rc) {
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}

© Cranes Varsity V2.1-2020-21 124/ 126


C++ www.cranesvarsity.com

// free attribute and wait for the other threads


pthread_attr_destroy(&attr);
for( i = 0; i < NUM_THREADS; i++ ) {
rc = pthread_join(threads[i], &status);

if (rc) {
cout << "Error:unable to join," << rc << endl;
exit(-1);
}
cout << "Main: completed thread id :" << i ;
cout << " exiting with status :" << status << endl;
}

cout << "Main: program exiting." << endl;


pthread_exit(NULL);
}
When the above code is compiled and executed, it produces the following result −
main() : creating thread, 0
main() : creating thread, 1
main() : creating thread, 2
main() : creating thread, 3
main() : creating thread, 4
Sleeping in thread
Thread with id : 0 .... exiting
Sleeping in thread
Thread with id : 1 .... exiting
Sleeping in thread
Thread with id : 2 .... exiting
Sleeping in thread
Thread with id : 3 .... exiting
Sleeping in thread
Thread with id : 4 .... exiting
Main: completed thread id :0 exiting with status :0
Main: completed thread id :1 exiting with status :0
Main: completed thread id :2 exiting with status :0
Main: completed thread id :3 exiting with status :0
Main: completed thread id :4 exiting with status :0
Main: program exiting.

**********

© Cranes Varsity V2.1-2020-21 125/ 126


C++ www.cranesvarsity.com

© Cranes Varsity V2.1-2020-21 126/ 126

You might also like