C++ Notes
C++ Notes
Day 1
Quick Review of C programming language
History
Inventor: Dennis Ritchie
Location: At&T Bell Lab
Development Year: 1969-1972
Operating System: Unix
Hardware: PDP-11
C is statically type checked as well as strongly type checked language.
C is a general purpose programming language.
Extension: .c
Standardization: ANSI
C89
C95
C99
C11
C17
C23
Data Type
Data Type Describe following things:
Size: How much memory is required to store the data.
Nature: Which type of data is allowed to stored inside memory
Operation: Which operations are allowed to perform on the data stored inside memory
Range: How much data is allowed to store inside memory
Types:
Fundamental Data Types( 5 )
void
char
int
float
double
Derived Data Types
Array
Function
Pointer
User Defined Data Types
Structure
Union
Type Modifiers
short
long
signed
unsigned
Type Qualifiers
const
volatile
Entry Point Function
According to ANSI specification, entry point function should be "main".
Syntax: 1
Syntax: 2
Syntax: 3
Syntax: 4
Syntax: 5
int main( void ){
return 0;
}
Syntax: 6
Syntax: 7
void main( ){
Comments
If we want to maintain documentation of the source code then we should use comments.
Comments in C/C++
Single Line Comment
/*
This is multiline comment
*/
Linker Error
Without definition, If we try to use function then linker generates error.
Example 2
Example 3
During function definition, if we use variables then it is called as function parameter or simply
parameter.
Example 1:
Example 2
Example 3
Example 4
Example 5
int number;
number = 10; //Assignment
Example 2:
Day 2
Function Activation Record
Pointer
Variable Definition:
An entity whose value can be change is called as variable.
Named memory location / name given to memory location is called as variable.
Variable is also called as identifier.
Assignement:
Identify the rules for variable/identifier name.
Pointer is a variable which is designed to store address of another variable.
Size of pointer:
16-bit : 2 bytes
32-bit : 4 bytes
64-bit : 8 bytes
Pointer Declaration:
Example 1
Example 2
Example 3
int *ptrNumber; //OK: Recommended
Example 4
#define NULL 0
Constant Qualifier
const is a keyword in C/C++ and it is consider as type qualifier.
Example 1
#include<cstdio>
int main( void ){
int number = 10; //Initialization
printf("Number : %d\n", number); //10
number = number + 5;
printf("Number : %d\n", number); //15
return 0;
}
If we dont want to modify value of the variable then we should use const qualifier.
Example
#include<cstdio>
int main( void ){
const int number = 10; //Initialization
printf("Number : %d\n", number); //10
//number = number + 5; //Not OK
return 0;
}
We can not modiy value of constant variable but we can read its value. Hence it is called as read-only
variable.
Constant and Pointer combinations
int *ptrNumber
Here ptrNumber is non constant pointer variable which can store address of non constant integer
variable.
Example:
printf("---------\n");
printf("---------\n");
Lab Assignment
Write a menu driven program to test accept/print employee record.
Define structure:
Employee:
name: char[ 30 ]
empid: int
salary: float
Create object and test the functionality
int main( void )
void accept_record( struct Employee *ptr );
void print_record( struct Employee *ptr );
Structure
Structure is derived data type in C/C++. But generally it is called as user defined data type.
If we want to group related data elements together then we should use structure.
Consider below examples
name:char[30], empid:int, salary:float: Employee
number:int, balance:float, type:char[30]: BankAccount
day:int, month:int, year:int: Date
hour:int, minute:int, second:int : Time
red:int , green:int, blue:int : Color
struct is keyword in C/C++.
To declare structure and to create object of the structure we must use struct keyword.
Example 1:
struct Employee{
char name[ 30 ]; //structure member
int empid; //structure member
float salary; //structure member
};
struct Employee emp;
//struct Employee : Type Name
//emp: object
If we want to give another name to the existing data type then we should use typedef.
typedef is a keyword.
Example 2:
struct Employee{
char name[ 30 ]; //structure member
int empid; //structure member
float salary; //structure member
};
typedef struct Employee Employee_t;
struct Employee emp1; //OK
Employee_t emp2; //OK
struct Employee_t emp3; //NOT OK
Example 3:
printf("Name : ");
scanf("%s",name);
printf("Empid : ");
scanf("%d",&empid);
printf("Salary : ");
scanf("%f", &salary);
printf("Name : ");
scanf("%s",emp.name);
printf("Empid : ");
scanf("%d",&emp.empid);
printf("Salary : ");
scanf("%f", &emp.salary);
printf("Name : ");
scanf("%s",ptr->name);
printf("Empid : ");
scanf("%d",&ptr->empid);
printf("Salary : ");
scanf("%f", &ptr->salary);
Reference: https://grandidierite.github.io/structure-alignment-and-packing-in-C-programming/
Day 3
Limitations with C programming languages
In C languages, all the functions are global. Any global function can access any global data. Hence
achieving data security is difficult.
There is no string data type in C hence string memory managment is difficult
If number of lines gets increased then code management becomes difficult.
C++ History
Inventor: Bjarne Stroustrup
Development Year: 1979
Initial name : C with Classes
Renamed in 1983 by ANSI: C++
Standardization: ISO Working Group
C++ Standards:
C++98
C++03
C++11
C++14
C++17
C++20
C++23
C++26
C++ is object oriented programming language.
C++ is derived from C and Simula( First object oriented programming language ).
C++ is also called as hybrid programming language.
C++ is statically as well as strongly type checked language.
Data Types
Fundamental Data Types( 7 )
void
bool
char
wchar_t ( typedef unsigned short wchar_t )
int
float
double
Derived Data Types( 4 )
Array
Function
Pointer
Reference
User Defined Data Types( 3)
Structure
Union
Class
Type Modifiers
short
long
signed
unsigned
Type Qualifiers
const
volatile
Execution Flow
cfront is translator developed by Bjarne strostrup. It was used to convert C++ source code into C
source code.
Name of the C++ compiler for linus is g++.
Access Specifier
If we want to control visibility of the members of structure/class then we should use access specifier.
Access specifiers in C++:
private
protected
public
Structure in C++
We can define function inside structure.
To create object of structure keyword struct is optional.
Structure members are by default considered as public.
Structure is not an object oriented concept.
What is the difference between structure and class?
structure members are by default public whereas class members are by default private.
Data Member
Variable declared inside class / structure is called as data member.
class Employee{
private:
char name[ 30 ]; //Data member
int empid; //Data member
float salary; //Data member
};
class Employee{
public:
void accept_record( void ){ //Member function
printf("Name : ");
scanf("%s", name );
printf("Empid : ");
scanf("%d", &empid );
printf("Salary : ");
scanf("%f", &salary );
}
Message Passing
Process of calling member function on object is called as message passing.
return 0;
}
emp.Employee::acceptRecord( ); //OK
emp.Employee::printRecord( ); //OK
return 0;
}
#ifndef EMPLOYEE_H_
#define EMPLOYEE_H_
//TODO: Declaration
#endif /* EMPLOYEE_H_ */
What is the difference between #include<abc.h> and #include"abc.h"
Standard directory for standard header file : C:\MicGW\include
If we include header file in angular bracket( < > ) then preprocessor try to locate that file inside
standard directory only.
Example: #include<stdio.h>
If we include header file in double quotes( " " ) then preprocessor first try to locate that file inside
current project directory. If not found then it will try to locate it from standard directory.
Example:
#include<stdio.h>
#include"stdio.h"
Storage Classes
In C/C++ there are 4 storage classes:
auto
register
static
extern
Storage class decide scope and lifetime of the elements
Scope
Scope of the variable / function describes area / region / boundry where we can access it.
Scope in C
Block Scope
Function Scope
Function Prototype Scope
File Scope
Consider below example:
Scope in C++
Block Scope
Function Scope
Function Prototype Scope
Enumeration Scope
Class Scope
Namespace Scope
File Scope
Program Scope
What is the difference between non static global variable and static global variable?
We can access non static global variable inside same file where it is declared as well as inside diffrent
file using extern keyword.
We can access static global variable inside same file where it is declared. But we can not access it
inside diffrent file. We will get linker error.
Lifetime
Lifetime describes time i.e how long object will be exist inside memory.
Lifetime in C/C++
Automatic Lifetime
All the local variables are having automatic lifetime.
Static Lifetime
All the static and global variables are having static lifetime
Dynamic Lifetime
All the dynamic objects are having dynamic lifetime.
Namespace
We can not give same name to the multiple variables inside same scope.
We can give same name to the local variable as well as global variable.
If name of the local variable and global variable are same then preference will be given to the local
variable. Consider below code:
Using scope resolution operator, we can use value of global variable inside program.
{//Start of block
int num1 = 30;
printf("Num1 : %d\n", ::num1); //10
printf("Num1 : %d\n", num1); //30
}
return 0;
}
void print_message( ){
printf("Good Evening!!\n");
}
int main( void ){
print_message( ); //OK
::print_message( ); //OK
return 0;
}
namespace na{
int num1 = 10;
}
int main( void ){
printf("Num1 : %d\n", na::num1); //OK: 10
return 0;
}
Example 2:
namespace na{
int num1 = 10;
}
namespace nb{
int num1 = 20;
}
int main( void ){
printf("Num1 : %d\n", na::num1); //OK: 10
printf("Num1 : %d\n", nb::num1); //OK: 20
return 0;
}
Example 3:
namespace na{
int num1 = 10;
}
namespace na{
int num2 = 20;
}
int main( void ){
printf("Num1 : %d\n", na::num1); //OK: 10
printf("Num1 : %d\n", na::num2); //OK: 20
return 0;
}
Example 4:
namespace na{
int num1 = 10;
int num2 = 20;
}
namespace nb{
int num1 = 30;
int num3 = 40;
}
Example 5:
namespace na{
int num1 = 10;
int num2 = 20;
}
namespace na{
//int num1 = 30; //error: redefinition of 'num1'
int num3 = 30;
}
int main( void ){
printf("Num1 : %d\n", na::num1); //OK: 10
printf("Num2 : %d\n", na::num2); //OK: 20
printf("Num3 : %d\n", na::num3); //OK: 30
return 0;
}
We can not define namespace inside block scope / function scope or class scope. Namespace
definition must appear in either namespace scope or file/program scope.
Example 6:
//File Scope
namespace na{
int num2 = 20;
//Namespace scope
namespace nb{ //Nested namespace
int num3 = 30;
}
}
Example 8:
namespace na{
int num1 = 10;
}
Example 9:
namespace na{
int num1 = 10;
}
namespace nb{
int num1 = 20;
}
int main( void ){
using namespace na;
printf("Num1 : %d\n", num1 ); //10
Example 10:
namespace na{
int num1 = 10;
}
void show_record( ){
printf("Num1 : %d\n", na::num1);
}
void print_record( ){
printf("Num1 : %d\n", na::num1);
}
void display_record( ){
printf("Num1 : %d\n", na::num1);
}
int main( void ){
::show_record( );
::print_record( );
::display_record( );
return 0;
}
Example 11:
namespace na{
int num1 = 10;
}
void show_record( ){
using namespace na;
printf("Num1 : %d\n", num1);
}
void print_record( ){
using namespace na;
printf("Num1 : %d\n", num1);
}
void display_record( ){
using namespace na;
printf("Num1 : %d\n", num1);
}
int main( void ){
::show_record( );
::print_record( );
::display_record( );
return 0;
}
Example 12:
namespace na{
int num1 = 10;
}
using namespace na;
void show_record( ){
printf("Num1 : %d\n", num1);
}
void print_record( ){
::print_record( );
::display_record( );
return 0;
}
namespace na{
int num1 = 10;
}
using namespace na;
namespace nb{
void show_record( ){
printf("Num1 : %d\n", num1);
}
void print_record( ){
nb::print_record( );
nb::display_record( );
return 0;
}
Example 14:
namespace na{
int num1 = 10;
}
int main( void ){
printf("Num1 : %d\n", na::num1);
namespace nb = na; //Alias
printf("Num1 : %d\n", nb::num1);
return 0;
}
Day 4
Variable is a container which is used to store data in RAM.
File is a container which is used to store data in HDD.
Stream is an abstraction(object), which either produce( write) or consume(read) inform from source
to destination.
Console is also called as terminal = Keyboard + Monitor / Printer.
In C, Standard stream objects associated with Console:
stdin
Standard input stream associated with keyboard which is used to read data.
scanf("%d", &number);
//same as
fscanf( stdin, "%d", &number );
stdout
Standard output stream associated with monitor which is used write data.
printf("%d", number);
//same as
fprintf(stdout, "%d", number);
stderr
Standard output stream associated with monitor which is used write error.
As shown above, ostream is alias / another name given to the basic_ostream class.
cout is object of ostream class. It is external object declared in std namespace.
It represents monitor which is used to write data on monitor.
Example 1:
#include<cstdio>
#include<iostream>
int main( void ){
printf("Hello World\n");
#include<iostream>
int main( void ){
std::cout << "Hello World" << std::endl;
//or
Example 3:
#include<iostream>
int main( void ){
int num1 = 10;
int num2 = 20;
Example 4:
#include<iostream>
int main( void ){
int num1 = 10;
int num2 = 20;
Example 5:
#include<iostream>
int main( void ){
int num1 = 10;
int num2 = 20;
#include<cstdio>
#include<iostream>
int main( void ){
int num1;
//In C programming language
printf("Num1 : ");
scanf("%d", &num1 );
#include<iostream>
int main( void ){
int num1;
//or
using namespace std;
cout << "Num1 : ";
cin >> num1;
return 0;
}
Example 3
#include<iostream>
int main( void ){
int num1, num2;
Example 4
#include<iostream>
int main( void ){
using namespace std;
int num1;
cout << "Num1 : ";
cin >> num1;
int num2;
cout << "Num2 : ";
cin >> num2;
cout << "Num1 : " << num1 << endl;
cout << "Num2 : " << num2 << endl;
return 0;
}
int num2;
cout << "Num1 : ";
cin >> num2;
clog << "Denominator is accepted" <<endl;
if( num2 == 0 ){
cerr << "Value of denominator is 0" <<endl;
clog << "Can not calculate Result because value of denominator is
0." <<endl;
}else{
int result = num1 / num2;
clog << "Result is calculated" <<endl;
cout<< "Result : "<< result << endl;
clog << "Result is printed" <<endl;
}
return 0;
}
Lab Assignment:
Class : Date
Data Member:
day: int
month: int
year: int
Member Function
void acceptRecord
void printRecord
void addDays( int count );
bool validateDate( );
Coding Convention
Pascal Case Convention
Consider examples:
Date
StringBuffer
NullPointerException
ArrayIndexOutOfBoundsException
In this case, including first word, first character of each word should be in upper case.
In C++, we will use this convention for giving name to:
Type Names( enum, union, structure, class )
File Name
Camel Case Convention
Consider examples:
main
parseInt;
showInputDialog
addNumberOfDays
In this case, excluding first word, first character of each word should be in upper case.
In C++, we will use this convention for giving name to:
Data member
Member function
local variable and function parameter
Snake Case Convention
Consider examples:
accept_record
print_record
In this case, mulitple word names are joined using underscore.
In C++, we will use this convention for giving name to:
global function
constant
macro
Hungerian Notation
It is convention recommended for C/C++.
Consider examples:
int iNum1;
double dNum2;
char szText[ 100 ];
Object oriented concepts
Only data members get space inside object. Member function do not get space inside object.
Data members of the class get space once per object according their order of declaration inside
class.
Member function do not get space inside object, rather all the objects of same class share single
copy of it.
Size of object depdends on size of all the data members declared inside class.
Characteristics of Object
State:
Value stored inside object is called as state of the object.
Value of the data member represents state of the object.
Behavior
Set of operations which are allowed to perform on object is called behavior of the object.
Member function defined inside class represents behavior of the object.
Idenitity
Value of any data member, which is used to identify object uniquely, is called as identity of the
object.
When state of objects are same then its address can be considered as its identity.
Class
Definition:
Class is collection of data members and member function.
Structure and behaviour of the object depends on class. Hence class is considered as a
template / model / blueprint for object.
Class represents, group of objects which is having common structure and common behavior.
Class is an imaginary / logical entity.
Example: Book, Laptop, Mobile Phone, Car.
Class implementation represents encapsulation.
Object
Definition:
Object is instance/variable of a class.
An entity which is having physical existance is called as object.
An entity, which is having state, behavior and identity is called as object.
Object is real time / physical entity.
Example: "More Effective C++", "MacBook Air", "iPhone 15", "Skoda Kushaq".
Instantiation represents abstraction.
Day 5
Empty class
A class which do not contain any member is called as empty class.
Consider example:
class Test{
};
Rule 2:
If we want to give same name to the function and if number of parameters are same then type
of at least one parameter must be different.
Rule 3:
If we want to give same name to the function and if number of parameters are same then order
of type of parameters must be different.
Rule 4
Only on the basis of different return type, we can not give same name to the function.
Definition:
When we define multiple functions with the help of above 4 rules then process is called as
function overloading.
Process of defining functions with same name and different signature is called as function
overloading.
Functions which take part into overloading are called as overloaded functions.
If implementation of functions are logically same / equivalent then we should overload
function.
In C++ we can overload:
global function
member function
constructor
static member function
constant member function
virtual member function
In C++ we can not overload:
main function
destructor
Per project, we can define only one main function. Hence we can not overload main function in C++.
Since destructor do not take any parameter, we can not overload destructor.
Why retrun type is not considered in function overloading:
Since catching value from function is optional, return type is not considered in function overloading.
Function overloading twister
void print( int number ){
cout << "int : " << number <<endl;
}
return 0;
}
return 0;
}
Process or algorithm which generates mangled name is called as name mangling.
ISO has not defined any specification on mangled name hence it may vary from compiler to compiler.
Using extern "C", we can invoke, C language function into C++ source code.
If we declared any function using exten "C" then compiler do not generate mangled name for it.
Consider ArithmeticOperation Header file()
#ifndef ARITHMETIC_OPERATION_H_
#define ARITHMETIC_OPERATION_H_
#endif
#ifndef CALCULATOR_H_
#define CALCULATOR_H_
extern "C"{
int sum( int num1, int num2 );
#endif
#include<iostream>
using namespace std;
#include"../include/ArithmeticOperation"
#include"../include/Calculator"
Default Argument
Consider following code:
#include<iostream>
using namespace std;
In C++, we can assign default value to the parameter of function. It is called as default argument.
Using default argument, we can reduce developers effort.
Default value can be:
constant
variable
macro
Example 1:
void sum( int num1, int num2, int num3 = 0, int num4 = 0, int num5 = 0
){
int result = num1 + num2 + num3 + num4 + num5;
cout << "Result : " << result << endl;
}
Example 2
int defaultArgument = 0;
void sum( int num1, int num2, int num3 = defaultArgument, int num4 =
defaultArgument, int num5 = defaultArgument ){
int result = num1 + num2 + num3 + num4 + num5;
cout << "Result : " << result << endl;
}
Example 3:
#define DEFAULT_VALUE 0
void sum( int num1, int num2, int num3 = DEFAULT_VALUE, int num4 =
DEFAULT_VALUE, int num5 = DEFAULT_VALUE ){
int result = num1 + num2 + num3 + num4 + num5;
cout << "Result : " << result << endl;
}
#include<iostream>
using namespace std;
#define DEFAULT_VALUE 0
void sum( int num1, int num2, int num3 = DEFAULT_VALUE, int num4 =
DEFAULT_VALUE, int num5 = DEFAULT_VALUE );
void sum( int num1, int num2, int num3, int num4, int num5 ){
int result = num1 + num2 + num3 + num4 + num5;
cout << "Result : " << result << endl;
}
this pointer
Software development life cycle:
Requirement
Analysis
Design
Implementation / Coding
Testing
Deployment / Installation
Maintenance
Problem Statment: Write a program to test functionality( accept and print record ) of complex
number.
Analysis
class Complex:
real number : int
imag number : int
Understand problem statement and do analysis from object oriented point of view. In other words,
decide class and data members for it.
Create object of the class
Inside object only data member will get space.
To process state of the object we should call and define member function.
If we call member function on object then compiler implicitly pass, address of current/calling object
as a argument to the member function. To catch/accpet address, compiler implicitly declare/create
one paramater inside member function. Such parameter is called as this pointer.
this is a keyword in C++.
Parameter do not get space inside object. Since this pointer is a function parameter, it doesnt
get space inside object.
this pointer get space once per function call on stack section / segment.
this pointer is a constant pointer. General type of this pointer is:
To access members of the class, use of this keyword is optional. If we do not use this then
compiler implicitly use this keyword.
Using this pointer, data member and member function can communicate with each other.
Hence this pointer is considered as a link / connection between them.
Following functions do not get this pointer:
Global function
Static member function
Friend function
this pointer is considered as first parameter of the member function.
class Complex{
private:
int real;
int imag;
public:
void acceptRecord( /* Complex *const this, */ int n1, int n1 ){
cout << "Enter real number : ";
cin >> this->real;
cout << "Enter imag number : ";
cin >> this->imag;
}
};
int main( void ){
Complex c1;
c1.acceptRecord( 10, 20 ); //c1.acceptRecord( &c1, 10, 20 );
return 0;
}
Definition:
this pointer is implicit pointer, which is available in every non static member function of
the class and which is used to store address of current / calling object.
If name of data member and local variable / function parameter is same then preference will be
given to local variable. In this case we should use this pointer before data members.
class Complex{
private:
int real;
int imag;
public:
//Complex *const this = &c1
void setReal( int real ){
this->real = real;
}
};
int main( void ){
Complex c1;
c1.setReal( 10 ); //c1.setReal( &c1, 10 );
return 0;
}
class Complex{
private:
int real;
int imag;
public:
//Complex *const this = &c1
int getReal( void ){
return this->real;
}
//Complex *const this = &c1
void setReal( int real ){
this->real = real;
}
//Complex *const this = &c1
int getImag( void ){
return this->imag;
}
//Complex *const this = &c1
void setImag( int imag ){
this->imag = imag;
}
};
int main( void ){
Complex c1;
c1.setReal( 10 );
c1.setImag( 20 );
return 0;
}
int main1( void ){
Complex c1;
c1.setReal( 10 );
c1.setImag( 20 );
A member function of class, which is used to read state of the object is called as inspector / selector /
getter function.
A member function of class, which is used to modify state of the object is called as mutator / modifier
/ setter function.
Day 6
Constructor
Member function of a class which is used to initialize the object is called as constructor.
Note: Constructor do not create object rather it initializes object.
Due to below reasons constructor is considered as special function of the class:
Its name is always same as class name.
It does not have any return type
It is designed to call implicitly
It gets called once per instance.
We can not call constructor on object, pointer or reference explicitly.
Example 1:
Complex c1;
c1.Complex( ); //Not OK
Example 2:
Complex c1;
Complex *ptr = &c1; //ptr is pointer
ptr->Complex( ); //Not OK
Example 3:
Complex c1;
Complex &c2 = c1; //c2 is reference
c2.Complex( ); //Not OK
If we create object without passing argument, then compile invoke parameterless constrctor.
Example:
Parameterized constructor
Constructor of the class which is having parameter(s) is called as parameterized constructor.
Example:
class Complex{
private:
int real;
int imag;
public:
Complex( ){ //Parameterless constructor
this->real = 0;
this->imag = 0;
}
Complex( int real, int imag ){ //Parameterized constructor
this->imag = real;
this->imag = imag;
}
};
Default constructor
If we do not define constructor inside class then compiler generate constructor for the class. Such
constructor is called as default constructor.
Compiler never generate parameterized constructor. In other words, compiler generated constructor
is zero argument / parameterless constructor.
Example:
class Complex{
};
int main( void ){
Comple c1; //On c1 Default constructor will call
Plain Old Data ( POD ) structure is also called as aggregate class in C++.
Aggregate class class following properties:
It does not contain private or protected non static data member.
It does not contain any user defined constructor.
It does not have base class
It does not contain virtual function
Aggregate initialization:
class Complex{
public:
int real;
int imag;
public:
void printRecord( void ){
cout << "Real Number : " << this->real << endl;
cout << "Imag Number : " << this->imag << endl;
}
};
int main( void ){
Complex c1{ 10, 20 }; //Aggregate initialization
return 0;
}
Miscellaneous
Consider below code:
class Complex{
private:
int real;
int imag;
public:
Complex( void ){
this->real = 0;
this->imag = 0;
}
Complex( int value ){
this->real = value;
this->imag = value;
}
Complex( int real, int imag ){
this->real = real;
this->imag = imag;
}
void printRecord( void ){
cout << "Real Number : " << this->real << endl;
cout << "Imag Number : " << this->imag << endl;
}
};
Complex c1;
Here on c1 object, parameterless constructor will call.
Complex c2( 10 );
Here on c2 object, single parameter constructor will call.
Complex c3( 10, 20 );
Here on c2 object, 2 parameter constructor will call.
Complex c4( );
It is declaration of c4 function which do not take any parameter and return object or Complex
type.
Constructor will not call here.
Complex c5 = 30;
It is same as Complex c5( 30 ).
Hence on c5, single parameter constructor will call.
Complex( 40, 50 );
It is anonymous object.
On object, 2 parameter constructor will call.
Complex c6 = 60, 70;
Compiler error.
Complex c7{ 80, 90 };
class Complex is not agrregate type. Hence it is compiler error.
Array Of Objects
Example 1:
Complex arr[ 3 ];
for( int index = 0; index < 3; ++ index )
arr[ index ].printRecord( );
Example 2:
#include<iostream>
using namespace std;
class Test{
private:
int num1;
int num2;
int num3;
public:
Test( void ) : num1( 10 ), num2( 20 ), num3( 30 ) {
}
Test( int num1, int num2, int num3 ) : num1( num1 ), num2( num2 ),
num3( num3 ) {
}
void printRecord( void ){
cout << "Num1 : " << this->num1 << endl;
cout << "Num2 : " << this->num2 << endl;
cout << "Num3 : " << this->num3 << endl;
}
};
int main( void ){
Test t1;
t1.printRecord( );
If we want to separate declaration and definition then constructor member initializer list must appear
in definition part.
Example 2:
#include<iostream>
using namespace std;
class Test{
private:
int num1;
int num2;
int num3;
public:
Test( void );
};
Test::Test( void ) : num1( 10 ), num2( 20 ), num3( 30 ) {
}
Test::Test( int num1, int num2, int num3 ) : num1( num1 ), num2(
num2 ), num3( num3 ) {
}
void Test::printRecord( void ){
cout << "Num1 : " << this->num1 << endl;
cout << "Num2 : " << this->num2 << endl;
cout << "Num3 : " << this->num3 << endl;
}
int main( void ){
Test t1;
t1.printRecord( );
Example 3:
#include<iostream>
using namespace std;
class Test{
private:
int num1;
int num2;
int num3;
public:
Test( int num1 = 0, int num2 = 0, int num3 = 0 ) ;
};
Test::Test( int num1, int num2, int num3 ) : num1( num1 ), num2(
num2 ), num3( num3 ) {
}
void Test::printRecord( void ){
cout << "Num1 : " << this->num1 << endl;
cout << "Num2 : " << this->num2 << endl;
cout << "Num3 : " << this->num3 << endl;
}
int main( void ){
Test t1;
t1.printRecord( );
Example 4:
#include<cstring>
#include<iostream>
#include<iomanip>
using namespace std;
class Employee{
private:
char name[ 30 ];
int empid;
float salary;
public:
Employee( const char *name = "", int empid = 0, float salary =
0.0f );
return 0;
}
#include<iostream>
using namespace std;
class Test{
private:
int number;
public:
Test( void ){
this->number = 0;
this->number = this->number + 10; //OK
}
void showRecord( void ){
this->number = this->number + 2; //OK
cout << "Number : " << this->number << endl;
}
void printRecord( void ){
this->number = this->number + 3; //OK
cout << "Number : " << this->number << endl;
}
};
int main( void ){
Test t;
t.showRecord( ); //12
t.printRecord( ); //15
t.printRecord( ); //18
t.showRecord( ); //20
return 0;
}
Once initialized, if we dont want to modify value of the data member inside any member function of
the class including constructor body then we should declare data member constant.
We can initialize non constant data member using constructor member initializer list or constructor
body but we must initialize constant data member using constructor member initializer list.
#include<iostream>
using namespace std;
class Test{
private:
const int number;
public:
Test( void ) : number( 10 ){
//this->number = this->number + 10; //Not OK
}
void showRecord( void ){
//this->number = this->number + 2; //Not OK
cout << "Number : " << this->number << endl;
}
void printRecord( void ){
//this->number = this->number + 3; //Not OK
cout << "Number : " << this->number << endl;
}
};
int main( void ){
Test t;
t.showRecord( ); //10
t.printRecord( ); //10
t.printRecord( ); //10
t.showRecord( ); //10
return 0;
}
In above statement, this pointer is constant pointer which can store address of any non
constant object.
It means that this pointer can contain address of only one object but using this pointer we can
modify state of the object.
Consider below statement:
In above statement, this pointer is constant pointer which can store address of any onstant
object.
It means that this pointer can contain address of only one object and using this pointer we can
not modify state of the object.
If we want to modify state of the non constant object inside member function then type of this pointer
should be "ClassName *const this" but If we dont want to modify state of the non constant object
inside member function then type of this pointer should be "const ClassName *const this".
If we dont want to modify state of the only current/calling object inside member function then we
should declare member function constant.
#include<iostream>
using namespace std;
class Test{
private:
int number;
public:
//Test *const this
Test( void ) : number( 10 ){
this->number = this->number + 10; //OK
}
//Test *const this
void showRecord( void ){
this->number = this->number + 2; //OK
cout << "Number : " << this->number << endl;
}
//const Test *const this
void printRecord( void )const{
//this->number = this->number + 3; //Not OK
cout << "Number : " << this->number << endl;
}
};
int main( void ){
Test t;
t.showRecord( ); //12
t.printRecord( ); //12
t.printRecord( ); //12
t.showRecord( ); //14
return 0;
}
Note: Only state of current object will not be changed inside constant member function. Other object
can be modifed inside constant member function.
#include<iostream>
using namespace std;
class Test{
private:
int number;
public:
//Test *const this
Test( void ) : number( 10 ){
this->number = this->number + 10; //OK
}
//Test *const this
void showRecord( void ){
this->number = this->number + 2; //OK
cout << "Number : " << this->number << endl;
}
//const Test *const this
void printRecord( void )const{
Test t;
t.number = 20; //OK
t.showRecord( ); //It will print 22
}
};
int main( void ){
Test t;
t.printRecord( );
return 0;
}
On non constant object, we can call constant member function as well as non constant member
function.
Below functions are not allowed to declare as constant:
Global function
Static Member Function
Constructor
Destructor
Since main function is global function, we can make it constant.
Why we can not declare global function constant?
According to concept, if we dont want to modify state of the current object inside member function
then we should declare member function constant.
In other words, constant member function is designed to call on object.
Since global function is not designed to call on object, we can not make it constant.
mutable keyword
Exceptionlly, if we want to modify state of non constant data member inside constant member
function then we should declare that data member mutable.
Consider below code:
#include<iostream>
using namespace std;
class Test{
private:
int num1;
int num2;
mutable int num3;
public:
Test( void ) : num1( 10 ), num2( 20 ), num3( 0 ){
}
void printRecord( void )const{
//this->num1 ++; //Not OK
cout<< "Num1 : " << this->num1 << endl;
//this->num2 ++; //Not OK
cout<< "Num2 : " << this->num2 << endl;
this->num3 ++; //OK
cout<< "Num3 : " << this->num3 << endl;
}
};
int main( void ){
Test t1;
t1.printRecord( );
return 0;
}
Constant Object
If we want some objects to be constant and some objects to be non constant then we should use
constant keyword.
Example:
On non constant object we can call constant as well as non constant member function.
On constant object, we can call only constant member function.
#include<iostream>
using namespace std;
class Test{
private:
int number;
public:
//Test *const this
Test( ) : number( 0 ){
}
Day 7
Typedef
typedef if a keyword in C/C++.
Using typedef we can not define new Type / new user defined data type.
If we want to give short and meaningful name then we should use typedef.
Using typedef we can create alias for Type / class not for object.
Example 1:
Example 2:
Example 3:
Example 4:
Reference
Consider below example:
int num1 = 10; //Initialization
int num2 = num1; //Initialization
++ num1; //11
++ num2; //12
cout<<"Num1 : "<< num1<<endl; //12
cout<<"Num2 : "<< num2<<endl; //12
return 0;
}
We can create multiple references to the same memory location. Consider below code:
Example 2
++ num1; //11
++ num2; //12
++ num3; //13
Example 3
++ num2; //OK: 11
//++ num3; //Not OK
We can create pointer to pointer but we can not create reference to reference.
Example 5:
++ num1; //11
++ num2; //12
++ num3; //13
class Test{
private:
char &ch;
public:
Test( char &ch2 ) : ch( ch2 ){
}
};
int main( void ){
char ch1 = 'A';
Test t( ch1 );
size_t size = sizeof( t );
cout << "Size : " << size << endl;
return 0;
}
#include<iostream>
using namespace std;
We can not create array of references but we can create reference to array.
Example:
#include<iostream>
using namespace std;
Exception Handling
If we make some syntatical mistake in the code then compiler generates error.
Example:
Without definition, if we try to access any member then linker generates error.
Example 1:
Example 2:
void print( ); //Declration
int main( void ){
print( ); //Linker error
return 0;
}
Logical error is called bug. In other words, syntacticaly valid but logically invalid statment represents
bug.
Example 1:
Example 2:
Example 3:
int num1;
accept_record( num1 );
int num2;
accept_record( num2 );
try{
int result = num1 / num2;
print_record( result );
}
#include<iostream>
#include<string>
using namespace std;
class ArithmeticException{
private:
string message;
int lineNumber;
string functionName;
string fileName;
public:
ArithmeticException( string message, int lineNumber, string
functionName, string fileName )
: message( message ), lineNumber( lineNumber ), functionName(
functionName ), fileName( fileName ){
}
void printStackTrace( ){
cout << this->message <<" in " << this->fileName <<":"<< this-
>functionName <<" at line no. "<< this->lineNumber<<endl;
}
};
int num2;
accept_record( num2 );
List of type(s) of exception that we specify after function name using throw keyword is called as
exception specification list.
Its responsibility of C++ developer to specify exception specification list.
If type of thrown exception is not available in exception specification list then C++ runtime implicitly
give call to the std::unexpected function which internally give call to the std::terminate() function.
If we separate function declaration and definition then we should specify exception specification list
in declaration as well as definition.
Using throw keyword, we can rethrow exception from netsed catch block into outer catch block:
#include<iostream>
#include<string>
using namespace std;
int main( void ){
try{
try{
string ex("exception" );
throw ex;
}catch( string &ex ){
cout << "Inside nested catch block" <<endl;
throw; //Rethrow exception ex
}
}catch( string &ex ){
cout << "Inside outer catch block" <<endl;
}catch( ... ){
cout << "Inside outer generic catch block" <<endl;
}
return 0;
}
Outer catch block can handle exception thrown from inner try block. But inner catch block can not
handle exception thrown from outer try block.
Day 8
Dynamic Memory Management in C
Below functions are declared in stdlib header file.
void* malloc(size_t size);
void* calloc(size_t count, size_t size);
void* realloc(void *ptr, size_t size);
void free(void *ptr);
malloc, calloc and realloc are used to allocate memort whereas free function is used to deallocate
memory.
malloc function
malloc is a function declared in header file.
prototype:
It is designed to allocate memory for single variable / single object. But we can use it to allocate
memory for array too.
Using malloc function, we can allocate memory on only heap section.
Everything on heap section is anonymous. In other words, dynamic object created using malloc
is anonymous.
If we allocate memory using malloc function then memory gets initialized with garbage value.
If malloc function fails to allocate memory then it returns NULL.
malloc( 0 ):
Some implementations of malloc will return a null pointer when we request to allocate zero
bytes. This is a way to handle the situation gracefully and indicate that no memory has been
allocated.
In other implementations, malloc(0) may return a valid, non-null pointer that you can use to
manipulate memory. However, this can lead to unexpected behavior and should generally be
avoided because it doesn't allocate any usable memory.
memory allocated using malloc function should be deallocate using free() function.
Example 1:
void *ptr = malloc( 4 );
//or
void *ptr = malloc( sizeof( int ) );
Example 3:
calloc function
calloc is a function declared in header file.
prototype:
It is designed to allocate memory for array. But we can use it to allocate memory for variable / single
object too.
Using calloc function, we can allocate memory on only heap section.
If we allocate memory using calloc function then memory gets initialized with zero(0) value.
If calloc function fails to allocate memory then it returns NULL.
memory allocated using calloc function should be deallocate using free() function.
realloc function
realloc is a function declared in header file.
prototype:
The realloc() function tries to change the size of the allocation pointed to by ptr to size, and returns
ptr.
If there is not enough room to enlarge the memory allocation pointed to by ptr, realloc() creates a new
allocation, copies as much of the old data pointed to by ptr as will fit to the new allocation, frees the
old allocation, and returns a pointer to the allocated memory.
If ptr is NULL, realloc() is identical to a call to malloc() for size bytes.
If size is zero and ptr is not NULL, a new, minimum sized object is allocated and the original object is
freed.
memory allocated using realloc function should be deallocate using free() function.
free function
free is a function declared in header file.
prototype:
#include<iostream>
#include<cstdlib>
using namespace std;
//Dereferencing
*ptr = 123;
cout<<"Value : "<< *ptr << endl; //Dereferencing
Memory allocation and deallocation for single variable using calloc/free function.
#include<iostream>
#include<cstdlib>
using namespace std;
#include<iostream>
#include<cstdlib>
using namespace std;
//Dereferencing
ptr[ 0 ] = 10; //*( ptr + 0 ) = 10
ptr[ 1 ] = 20; //*( ptr + 1 ) = 20
ptr[ 2 ] = 30; //*( ptr + 2 ) = 30
//Dereferencing
for( int index = 0; index < 3; ++ index )
cout << ptr [ index ] <<endl; //cout << *( ptr + index ) <<endl;
#include<iostream>
#include<cstdlib>
using namespace std;
//Dereferencing
ptr[ 0 ] = 10; //*( ptr + 0 ) = 10
ptr[ 1 ] = 20; //*( ptr + 1 ) = 20
ptr[ 2 ] = 30; //*( ptr + 2 ) = 30
//Dereferencing
for( int index = 0; index < 3; ++ index )
cout << ptr [ index ] <<endl; //cout << *( ptr + index ) <<endl;
Memory allocation and deallocation for multidimensional array using malloc/free function.
#include<iostream>
#include<cstdlib>
using namespace std;
Memory allocation and deallocation for multidimensional array using calloc/free function.
#include<iostream>
#include<cstdlib>
using namespace std;
int *ptr = new int; //Here memory will be initialized to garbage value
//int *ptr = ( int* )::operator new ( sizeof( int ) );
delete ptr;
//::operator delete( ptr );
Example 2:
int *ptr = new int( 123 ); //Here memory will be initialized to 123
value
//int *ptr = ( int* )::operator new ( sizeof( int ) );
delete ptr;
//::operator delete( ptr );
Example 3:
delete ptr;
//::operator delete( ptr );
```
Example 4:
int main( void ){
//Memory allocation for single integer variable
//int *ptr = new int; //Dyanamic memory allocation: Garbage Value
//int *ptr = new int( ); //Dyanamic memory allocation: 0
int \*ptr = new int( 123 ); //Dyanamic memory allocation: 123
Example 5
delete[ ] ptr;
//::operator delete[ ]( ptr );
Example 6:
//Memory Allocation
int *ptr = new int*[ 2 ];
for( int index = 0; index < 2; ++ index )
ptr[ index ] = new int[ 3 ];
//Memory Deallocation
for( int index = 0; index < 2; ++ index )
delete[] ptr[ index ];
delete[ ] ptr;
Example 7:
class Array{
private:
int arr[ 3 ];
};
int main( void ){
Array a1; //a1 is object
return 0;
}
Destructor
Destructor is a member function of the class which is used to release the resources hold by the
object.
Destructor do not deallocate memory of object.
Destructor is considered as special function of the class due to following reasons:
Its name is same as class name which precedes with tild( ~ ) operator.
It doesnt take any parameter or return any value.
It is designed to call implicitly.
Example:
#include<iostream>
using namespace std;
class Array{
private:
int size;
int *arr;
public:
//Array *const this = &a1
Array( void ){
this->size = 0;
this->arr = nullptr;
}
//Array *const this = &a1
Array( int size ){
cout << "Array( int size )" << endl;
this->size = size;
this->arr = new int[ size ];
}
//Array *const this = &a1
void acceptRecord( void ){
for( int index = 0; index < this->size; ++ index ){
cout << "Enter element : ";
cin >> this->arr[ index ];
}
}
//Array *const this = &a1
void printRecord( void ){
for( int index = 0; index < this->size; ++ index )
cout << this->arr[ index ] << endl;
}
//Array *const this = &a1
~Array( void ){ //Destructor
if( this->arr != nullptr ){
delete[] this->arr;
this->arr = nullptr;
}
}
};
int main( void ){
Array a1(3); //Static memory allocation for object
a1.acceptRecord( );
a1.printRecord( );
return 0;
}
If we do not provide destructor for the class then compiler provide one destructor for the class by
deafult. It is called default destructor.
Destructor calling sequence is exactly opposite of constructor calling sequence.
We can not declare destructor static/const/volatile. We can declare constructor as inline and virtual.
We can overload constructor but we can not overload destructor.
Note: Even though destructor is designed to call implicitly, we can call it explicitly too.
Example 2:
Example 3:
Copy constructor
If we try to intialize newly created object from exisiting object of same class then on newly created
object copy constructor gets called.
Example:
If we do not define copy constructor inside class, then compiler generate one copy constructor for
the class by default. It is called as default copy constructor.
Default copy constructor, by default creates shallow copy.
Copy constructor is a parameterized constructor of the class which take single parameter of same
type as a reference.
Copy constructor is not a new type of constructor. It is parameterized constructor.
Syntax:
class ClassName{
public:
//const ClassName &other = reference of existing object
//ClassName *const this = Address of newly created object
ClassName( const ClassName &other ){
//TODO: Shallow Copy or Deep Copy
}
};
Example:
class Complex{
private:
int real;
int imag;
public:
Complex( void ){
this->real = 0;
this->imag = 0;
}
//const Complex &other = c1;
//Complex *const this = &c3
Complex( const Complex &other ){ //Copy Constructor
this->real = other.real; //Shallow Copy
this->imag = other.imag; //Shallow Copy
}
Complex( int real, int imag ){
this->real = real;
this->imag = imag;
}
void printRecord( void ){
cout << "Real Number : " << this->real << endl;
cout << "Imag Number : " << this->imag << endl;
}
};
int main( void ){
Complex c1( 10, 20 ); //On C1, Parameterized ctor will call
Complex c2; //On c2, parameterless constructor will call
Complex c3 = c1; //On c3, copy constructor will call
return 0;
}
Example 4:
Friend function:
friend is keyword in C++.
If we want to access private and protected members of the class inside non member function then
we should declare non member function friend.
We can use friend keyword only inside class.
Example:
#include<iostream>
using namespace std;
class Test{
private:
int num1;
protected:
int num2;
public:
Test( void ){
this->num1 = 10;
this->num2 = 20;
}
friend int main( void ); //We can declare it in either
private/protected/public section
};
int main( void ){
Test t;
cout << "Num1 : " << t.num1 << endl;
cout << "Num2 : " << t.num2 << endl;
return 0;
}
If we declare function as a friend inside class then it is not considered as a member of a class.
Example:
#include<iostream>
using namespace std;
class Test{
private:
int num1;
protected:
int num2;
public:
Test( void ){
this->num1 = 10;
this->num2 = 20;
}
friend void print( );
};
void print( ){
Test t;
cout << "Num1 : " << t.num1 << endl;
cout << "Num2 : " << t.num2 << endl;
}
int main( void ){
//Test t;
//t.print( ); //Not OK
print( );
return 0;
}
#include<iostream>
using namespace std;
class A{
private:
int num1;
public:
A( void ){
this->num1 = 10;
}
friend void print( );
};
class B{
private:
int num2;
public:
B( void ){
this->num2 = 20;
}
friend void print( );
};
void print( ){
A a;
cout << "Num1 : " << a.num1 << endl;
B b;
cout << "Num2 : " << b.num2 << endl;
}
int main( void ){
print( );
return 0;
}
class A{
public:
void sum( void );
};
class B{
private:
int num1;
int num2;
public:
B( );
friend void A::sum( void );
};
B::B( void ){
this->num1 = 10;
this->num2 = 20;
}
void A::sum( void ){
B obj;
int result = obj.num1 + obj.num2;
cout << "Result : " << result << endl;
}
int main( void ){
A a;
a.sum( );
return 0;
}
If we want to access private members of the class inside all the member functions of another class
then we should declare class as a friend.
#include<iostream>
using namespace std;
class A{
public:
void sum( void );
void sub( void );
void multiplication( void );
};
class B{
private:
int num1;
int num2;
public:
B( );
/* friend void A::sum( void );
friend void A::sub( void );
friend void A::multiplication( void ); */
friend class A;
};
B::B( void ){
this->num1 = 10;
this->num2 = 20;
}
void A::sum( void ){
B obj;
int result = obj.num1 + obj.num2;
cout << "Result : " << result << endl;
}
void A::sub( void ){
B obj;
int result = obj.num1 - obj.num2;
cout << "Result : " << result << endl;
}
void A::multiplication( void ){
B obj;
int result = obj.num1 * obj.num2;
cout << "Result : " << result << endl;
}
int main( void ){
A a;
a.sum( );
a.sub( );
a.multiplication( );
return 0;
}
We can declare mutual friend classes but we can not declare mutual friend functions.
Example:
class A{
private:
int num2;
public:
A( void );
void showRecord( );
friend class B;
};
class B{
private:
int num1;
public:
B( void );
void displayRecord( );
friend class A;
};
A::A( void ){
this->num2 = 200;
}
B::B( void ){
this->num1 = 100;
}
void A::showRecord( void ){
B obj;
cout << "Num1 : " << obj.num1 <<endl;
}
void B::displayRecord( ){
A obj;
cout << "Num2 : " << obj.num2 <<endl;
}
int main( void ){
A a;
a.showRecord( );
B b;
b.displayRecord( );
return 0;
}
class Television{
friend class Remote;
}
class Remote{
};
#include<iostream>
using namespace std;
class Test{
private:
int num1;
int num2;
int num3;
public:
Test( void ){
this->num1 = 0;
this->num2 = 0;
this->num3 = 500;
}
Test( int num1, int num2 ){
this->num1 = num1;
this->num2 = num2;
this->num3 = 500;
}
void printRecord( void ){
cout << "Num1 : " << this->num1 << endl;
cout << "Num2 : " << this->num2 << endl;
cout << "Num3 : " << this->num3 << endl;
}
};
int main( void ){
Test t1( 10, 20 );
Test t2( 30, 40 );
If we want to share value of any data member in all the objects of same class then we should declare
data member static.
Static data member get space during program loading once per class on data segment.
if we create object of the class then only non static data member get space inside it. Hence size of
object depends on size of all the data members declared inside class.
Data member of the class which get space inside object is called as instance variable. In other words
non static member is also called as instance variable.
Instance variable get space once per object.
To access instance variable either we should use object or pointer/reference to that object.
Data member of the class which do not get space inside object is called as class level variable. In
other words static member is also called as class level variable.
Class level variable get space once per class.
To access class level variable we should class name and :: operator.
Example 1:
class A{
int n1;
int n2;
static int count;
};
int main( void ){
A a1,a2,a3;
return 0
}
Example 2:
class B{
int n3;
int n4;
static int count;
};
int main( void ){
B b1,b2,b3;
return 0
}
Example 3:
class C{
int n5;
int n6;
static int count;
};
int main( void ){
C c1,c2,c3;
return 0
}
if we want to declare data member static then we must provide global definition for it. Otherwisw
linker will generate error.
#include<iostream>
using namespace std;
class Test{
public:
int num1; //Instance variable
int num2; //Instance variable
static int num3; //Class level variable
public:
Test( void ){
this->num1 = 0;
this->num2 = 0;
}
Test( int num1, int num2 ){
this->num1 = num1;
this->num2 = num2;
}
void printRecord( void ){
cout << "Num1 : " << this->num1 << endl;
cout << "Num2 : " << this->num2 << endl;
cout << "Num3 : " << Test::num3 << endl;
}
};
int Test::num3 = 500; //Global definition
int main( void ){
Test t1;
t1.printRecord( );
return 0;
}
#include<iostream>
using namespace std;
class Test{
public:
int num1; //Instance variable
int num2; //Instance variable
const static int num3; //Class level variable
public:
Test( void ){
this->num1 = 0;
this->num2 = 0;
}
Test( int num1, int num2 ){
this->num1 = num1;
this->num2 = num2;
}
void printRecord( void ){
cout << "Num1 : " << this->num1 << endl;
cout << "Num2 : " << this->num2 << endl;
cout << "Num3 : " << Test::num3 << endl;
}
};
Day 10
Static member function
Example:
#include<iostream>
using namespace std;
class Test{
private:
int num1; //Non static data member / Instance variable
int num2; //Non static data member / Instance variable
static int num3; //Static data member / Class level variable
public:
Test( void ){
this->num1 = 0;
this->num2 = 0;
}
void setNum1( int num1 ){
this->num1 = num1;
}
void setNum2( int num2 ){
this->num2 = num2;
}
static void setNum3( int num3 ){
Test::num3 = num3;
}
void printRecord( void ){
cout << "Num1 : " << this->num1 << endl;
cout << "Num2 : " << this->num2 << endl;
cout << "Num3 : " << Test::num3 << endl;
}
};
int Test::num3 = 0; //Global definition
int main( void ){
Test t1;
t1.setNum1( 10 );
t1.setNum2( 20 );
Test::setNum3( 30 );
t1.printRecord( );
return 0;
}
int main1( void ){
Test t1;
//t1.num1 = 10; //error: 'num1' is a private member of 'Test'
//t1.num2 = 20; //error: 'num2' is a private member of 'Test'
//Test::num3 = 30;//error: 'num3' is a private member of 'Test'
t1.printRecord( );
return 0;
}
To access non static members of the class, we should define non static member function inside
class.
Non static member functions are designed to call on object. Hence it is also called as instace method.
Since non static member functions / instance methods are designed to call on object/instance, it gets
this pointer. Since non static member function get this pointer, we can access static as well as non
static members inside non static member function.
To access static member of the class, we should define static member function inside class.
Static member functions are designed to call on class name. Hence it is also called as class level
method.
Since static member functions / class level methods are designed to call on class name, it doesn't get
this pointer. Since static member function doesn't get this pointer, we can access only static
members inside static member function.
Static member function do not get this pointer but we can create object inside static member
function.
Using object, we can access non static members inside static member function.
Example:
class Test{
private:
int num1;
static int num2;
public:
Test( void ) : num1( 10 ){
}
static void print( void ){
Test t;
cout << "Num1 : " << t.num1 << endl;
cout << "Num2 : " << Test::num2 << endl;
}
};
int Test::num2 = 20; //Global definition
int main( void ){
Test::print( );
return 0;
}
#include<iostream>
using namespace std;
class Math{
public:
static const double PI;
public:
static double pow( double base, int index ){
double result = 1;
for( int count = 1; count <= index ; ++ count )
result = base * result;
return result;
}
};
const double Math::PI = 3.14;
int main( void ){
double result = Math::pow( 2.0, 3 );
cout << "Result : "<<result <<endl;
return 0;
}
#include<iostream>
using namespace std;
class InstanceCounter{
private:
static int count;
public:
InstanceCounter( ){
InstanceCounter::count = InstanceCounter::count + 1;
}
static int getCount( void ){
return InstanceCounter::count;
}
~InstanceCounter( ){
InstanceCounter::count = InstanceCounter::count - 1;
}
};
int InstanceCounter::count = 0;
int main( void ){
InstanceCounter c1, c2, c3;
cout << "Instance Counter : " << InstanceCounter::getCount( ) <<
endl;
return 0;
}
Anonymous class
#include<iostream>
using namespace std;
Operator Overloading
Token
Token is a basic unit of a program.
Classification of tokens:
Identifier
Keywords
Constant
Operator
Separator / punctuators
Classfication of Operators
Unary Operators
An operator which requires only one operand( e.g sizeof( a ) ) is called as unary operator.
Example: sizeof, typeid, ++, --, !( Logical NOT ), ~, +, -, * etc
Binary Operators
An operator which requires two operands( e.g a + b ) is called as binary operator.
Arithmetic operators
+, -, *, /, %
Relational operators
<, <= >, >=, ==, !=
Logical operators
&&( Logical AND), || ( Logical OR ),
Bitwise operators
&( Bitwise AND), | ( Bitwies OR), ^( Bitwise XOR), <<, >>
Assignment operators
=, short hand operators( +=, -=, *= etc )
Ternary Operators
An operator which requires three operands is called as ternary operator.
Conditional operator( ? : )
Consider code in C programming language:
Example 1:
struct Point{
int xPos;
int yPos;
};
int main( void ){
struct Point pt1 = { 10, 20 }; //OK
struct Point pt2 = { 30, 40 }; //OK
struct Point pt3; //OK
pt3.xPos = pt1.xPos + pt2.xPos; //OK
pt3.yPos = pt1.yPos + pt2.yPos; //OK
return 0;
}
int main1( void ){
struct Point pt1 = { 10, 20 }; //OK
struct Point pt2 = { 30, 40 }; //OK
struct Point pt3; //OK
pt3 = pt1 + pt2; //Not OK
return 0;
}
#include<iostream>
using namespace std;
class Complex{
private:
int real;
int imag;
public:
Complex( void ){
this->real = 0;
this->imag = 0;
}
Complex( int real, int imag ){
this->real = real;
this->imag = imag;
}
void printRecord( void ){
cout << "Real Number : " << this->real <<endl;
cout << "Imag Number : " << this->imag <<endl;
}
};
If we want to use operator with the objects of user defined type(structure, class etc. ) then we should
overload operator.
To overload operator, we should define operator function.
operator is keyword in C++ which is used to define operator function.
We can define operator function using 2 ways:
Member function
Non member function
By defining operator function, we are increasing capability of exsiting operators. This process of
givining extension to the meaning of the operator is called as operator overloading.
Consider Example using member function:
#include<iostream>
using namespace std;
class Complex{
private:
int real;
int imag;
public:
Complex( void ){
this->real = 0;
this->imag = 0;
}
Complex( int real, int imag ){
this->real = real;
this->imag = imag;
}
//Complex other = c2
//Complex *const this = &c1
Complex operator+( Complex other ){
Complex result;
result.real = this->real + other.real;
result.imag = this->imag + other.imag;
return result;
}
void printRecord( void ){
cout << "Real Number : " << this->real <<endl;
cout << "Imag Number : " << this->imag <<endl;
}
};
#include<iostream>
using namespace std;
class Complex{
private:
int real;
int imag;
public:
Complex( void ){
this->real = 0;
this->imag = 0;
}
Complex( int real, int imag ){
this->real = real;
this->imag = imag;
}
void printRecord( void ){
cout << "Real Number : " << this->real <<endl;
cout << "Imag Number : " << this->imag <<endl;
}
friend Complex operator+( Complex c1, Complex c2 );
};
Complex operator+( Complex c1, Complex c2 ){
Complex result;
result.real = c1.real + c2.real;
result.imag = c1.imag + c2.imag;
return result;
}
int main( void ){
Complex c1( 10, 20 );
Complex c2( 30, 40 );
Complex c3;
c3 = c1 + c2; // c3 = operator+( c1, c2 )
c3.printRecord( );
return 0;
}
Using operator overloading we can not create user defined operators rather we can increase
capability of existing operators.
Limitations of operator overloading
We can not overload below operators using member function as well as non member function
dot( .) / meber selection operator
.* ( pointer to member selection operator )
sizeof operator
::( scope resolution operator )
Conditional ( ? : ) / Ternary operator
typeid operator
static_cast operator
dyanmic_cast operator
const_cast operator
reinterpret_cast operator
We can not overload below operators using non member function but we can overload it using
member function
Assignment operator( = )
Index / subscript operator
Call / Function call operator[ () ]
Arrow( -> ) operator
using operator overloading, we can change meaning of the operator.
Arithmetic operator overloading
Example 1:
Example 2:
Example 3:
Example 4:
Example 5:
Example 6:
Example 7:
Example 8:
Example 2:
Complex c1( 10, 20 );
Complex c2( 10, 20 );
bool status = c1 == c2; //status = operator==( c1, c2 ); //Using non
member function
Example 3:
Example 4:
Example 5:
Example 6:
Example 7:
Example 8:
Complex c1( 10, 20 );
Complex c2( 10, 20 );
bool status = c1 > c2; //status = operator>( c1, c2 ); //Using non
member function
Example 2:
Example 3:
Example 4:
Complex c1;
cin >> c1; //cin.operator>>( c1 );
Here to accept record for c1, we should define operator>>() function inside istream class,
which is not recommended. So we will not overload it using member function.
Consider call using non member function:
Complex c1;
cin >> c1; //operator>>( cin, c1 );
Here to accept record for c1, we should define operator>>() function global, which is possible
for us. Hence we will overload operator >> using non member function.
Consider another example:
Complex c1;
Complex c2;
cin >> c1 >> c2; //operator>>( operator>>( cin, c1 ), c2 );
General Syntax:
class ClassName{
friend istream& operator>>( istream &cin, ClassName &other );
};
istream& operator>>( istream &cin, ClassName &other ){
//TODO: accept record using other reference variable
return cin;
}
Complex c1(10,20);
cout << c1; //cout.operator<<( c1 );
Here to print record of c1, we should define operator<<() function inside ostream class, which
is not recommended. So we will not overload it using member function.
Consider call using non member function:
Complex c1(10,20);
cout << c1; //operator<<( cout, c1 );
Here to print record of c1, we should define operator<<() function global, which is possible for
us. Hence we will overload operator << using non member function.
Consider another example:
General Syntax:
class ClassName{
friend ostream& operator<<( ostream &cout, ClassName &other );
};
ostream& operator<<( ostream &cout, ClassName &other ){
//TODO: print record using other reference variable
return cout;
}
If we "initialize" object from another object of same class the copy constructor gets called.
Syntax:
class ClassName{
public:
ClassName( const ClassName &other ){
//TODO: Shallow / Deep Copy
}
}
Example 3:
If we assign object to another object of same class the assignment operator function gets called.
If we do not define assignment operator function for the class then compiler generates one
assignment operator function for the class by default, it is called as default assignment operator
function.
Default copy constructor and default assignment operator function by default creates shallow copy.
Example 5:
Syntax:
class ClassName{
public:
ClassName& operator=( const ClassName &other ){
//TODO: Shallow / Deep Copy
return (*this);
}
}
If we want to use subscript operator with object at L.H.S of assignment operator then expression
should not return value. Rather it should return pointer/reference of the memory location.
Example:
a1[ 2 ] = 300;
//a1.operator[ ]( 2 ) = 300;
#include<iostream>
using namespace std;
class Array{
private:
int size;
int *arr;
public:
Array( void ){
this->size = 0;
this->arr = nullptr;
}
Array( int size ){
this->size = size;
this->arr = new int[ this->size ];
}
Array( const Array &other){
this->size = other.size;
this->arr = new int[ this->size ];
for( int index = 0; index < this->size; ++ index )
this->arr[ index ] = other.arr[ index ];
}
//const Array &other = a1
//Array* const this = &a2
Array& operator=( const Array &other ){
this->~Array( );
this->size = other.size;
this->arr = new int[ this->size ];
for( int index = 0; index < this->size; ++ index )
this->arr[ index ] = other.arr[ index ];
return(*this);
}
//Array *const this
int& operator[ ]( int index ){
return this->arr[ index ];
}
friend istream& operator>>( istream &cin, Array &other ){
for( int index = 0; index < other.size; ++ index ){
cout<<"Enter element : ";
cin >> other.arr[ index ];
}
return cin;
}
friend ostream& operator<<( ostream &cout, Array &other ){
for( int index = 0; index < other.size; ++ index ){
cout << other.arr[ index ] <<endl;
}
return cout;
}
~Array( void ){
if( this->arr != nullptr ){
delete[] this->arr;
}
this->arr = nullptr;
}
};
int main( void ){
Array a1( 3 );
a1[ 2 ] = 300;
//a1.operator[ ]( 2 ) = 300;
class Complex{
private:
int real;
int imag;
public:
Complex( void ) : real( 0 ), imag( 0 ){
}
void operator()( int real, int imag ) {
this->real = real;
this->imag = imag;
}
friend ostream& operator<<( ostream &cout, const Complex &other ){
cout << "Real Number : "<< other.real <<endl;
cout << "Imag Number : "<< other.imag <<endl;
return cout;
}
};
int main( void ){
Complex c1;
How will you swap 2 numbers without third variable( ref: use +/- or bitwise operator )
Template
In C++, if we want to write typesafe generic code then we should use template.
template is keyword in C++.
In C++, by passing data type as a argument, we can write generic code. Hence paramerized type is
called as template.
Example:
Process of identifing type and passing it as a argument implicilty to the function is called as type
inference.
swap_object<int>( a, b );
Class Template
#include<iostream>
#include<string>
using namespace std;
template<class T>
class Array{
private:
int size;
T *arr;
public:
Array( void ){
this->size = 0;
this->arr = nullptr;
}
Array( int size ){
this->size = size;
this->arr = new T[ this->size ];
}
Array( const Array &other){
this->size = other.size;
this->arr = new T[ this->size ];
for( int index = 0; index < this->size; ++ index )
this->arr[ index ] = other.arr[ index ];
}
//const Array &other = a1
//Array* const this = &a2
Array& operator=( const Array &other ){
this->~Array( );
this->size = other.size;
this->arr = new T[ this->size ];
for( int index = 0; index < this->size; ++ index )
this->arr[ index ] = other.arr[ index ];
return(*this);
}
//Array *const this
T& operator[ ]( int index ){
return this->arr[ index ];
}
~Array( void ){
if( this->arr != nullptr ){
delete[] this->arr;
}
this->arr = nullptr;
}
friend istream& operator>>( istream &cin, Array<T> &other ){
for( int index = 0; index < other.size; ++ index ){
cout<<"Enter element : ";
cin >> other.arr[ index ];
}
return cin;
}
friend ostream& operator<<( ostream &cout, Array<T> &other ){
for( int index = 0; index < other.size; ++ index ){
cout << other.arr[ index ] <<endl;
}
return cout;
}
};
int main( void ){
Array<string> a1( 3 );
// Book class
class Book {
private:
string title;
string author;
string ISBN;
string genre;
public:
// Constructor, getters, and setters
// Other methods like displaying book details
};
// Library class
class Library {
private:
string name;
string address;
vector<Book> books; // A library can contain multiple books
public:
// Constructor, methods to add and remove books, etc.
};
// Member class
class Member {
private:
string name;
int memberID;
vector<Book> borrowedBooks; // A member can borrow multiple
books
public:
// Constructor, methods to borrow and return books, etc.
};
int main() {
// Create a library
Library myLibrary("Central Library", "123 Main St");
// Create members
Member member1("Alice", 101);
Member member2("Bob", 102);
return 0;
}
Abstraction in C++:
Encapsulation
It is a major pillar of oops.
Definition:
Binding of data( data member ) and code( member function ) together is called as
encapsulation.
Implementation of abstraction is called as encapsulation.
Encapsulation in C++:
class Complex{
private:
int real;
int imag;
public:
void acceptRecord( void ){
//TODO
}
void printRecord( void ){
//TODO
}
};
Abstraction focuses on the observable behavior of an object, whereas encapsulation focuses on the
implementation that gives rise to this behavior.
Process of developing application with the help of small units/parts/module is called as modularity.
Main goal of modularity is to minimize module dependency.
In C++, you can achieve modularity through various mechanisms, including:
Functions
Classes and Objects
Namespaces
Header Files
Hierarchy
It is a major pillar of oops.
class Date{
private:
int day;
int month;
int year;
public:
//TODO: constructor(s)
//TODO: getters and setters
};
class Employee{
private:
string name;
int empid;
float salary;
Date joinDate; //Association
public:
//TODO: constructor(s)
//TODO: getters and setters
};
Composition
Consider below example:
Human has a heart
Dependent object: Human object
Dependency object: Heart object
In case of association, if dependency object do not exist without dependent object then it is called as
composition.
class Heart{
//TODO
};
class Human{
Heart hrt;
};
class Faculty{
//TODO
};
class Department{
Faculty faculty;
};
It represents loose coupling.
Day 12
Reference: https://www.eskimo.com/~scs/cclass/notes/top.html
size of std::string is 24 bytes.
Consider Matrix class object
Inheritance
Consider below examples:
Manager is a Employee
Book is a Product
Rectangle is a Shape
SavingAccount is a Account
Car is a Vehicle
If is a relationship is exist between the types then we should use inheritance.
Inheritance is also called as generalization.
Consider below code:
In C++, parent class is called as Base class and child class is called as Derived class.
If we create object of derived class then all the non static data member declared in base class and
derived class get space inside it. In other words, non static data member of the base class inherit into
derived class.
Using derived class, we can access static data member declared in base class. In other words, static
data member of base class inherit into derived class.
All the data members( static & non static of any access specifier ) of base class inherit into derived
class. But only non static data members get space inside object.
Data members of drived class, do not inherit into base class hence size of object of base class
depends on non static data members declared inside base class only.
Size of object of derived class = size of all the non static data members declared in base class and
derived class.
Note: private/protected/public data members( static & non static ) inherit into derived class.
We can call, non static member function of base class on object of derived class. In other words, non
static member function of base class inherit into derived class.
We can call, static member function of base class on derived class name. In other words, static
member function of base class inherit into derived class.
Below functions, do not inherit into derived class:
constructor
destructor
copy constructor
assignment operator function
friend function
Except above five functions, all the static and non static member functions of base class inherit into
derived class.
During inheritance, member functions of base class inherit into derived class. Hence using derived
class object, we can call member function of base class as well as derived class.
During inheritance, member functions of derived class do not inherit into base class. Hence using
base class object, we can call member function of base class only.
Nested class of base class inherit into derived class.
Final Conclusion: Except constructor, destructor, copy constructor, assignment operator function and
friend function all the members of base class inherit into derived class.
If we create object of Base class then only base class constructor gets called.
If we create object of Derived class, then first base class constructor gets called and the derived
class constructor gets called. Destructor calling sequence is exactly opposite of constructor calling
sequence.
From any constructor of deived class, by default, base class's parameterless constructor gets called.
Using constructor's base initializer list, we can call, any constructor of base class from constructor of
derived class.
Employee( string name, int age, int empid, float salary ) : Person(
name, age ){
cout << "Employee( string name, int age, int empid, float salary
)" << endl;
this->empid = empid;
this->salary = salary;
}
In above code, local variable is hiding global variable. It is also called as shadowing
Example 2:
class Test{
private:
int num1;
public:
Test( void ){
this->num1 = 10;
}
void print( void ){
int num1 = 20;
cout<<"Num1 : "<< num1 << endl; //20
}
};
int main( void ){
Test t;
t.print( ); //20
return 0;
}
In above code, local variable is hiding data member. It is also called as shadowing
Example 3:
class A{
public:
int num1;
public:
A( void ){
this->num1 = 10;
}
};
class B : public A{
public:
int num1;
public:
B( void ){
this->num1 = 20;
}
};
int main( void ){
B b;
cout << "Num1 : "<< num1 << endl; //20
return 0;
}
In above code, derived class data member is hiding base class data member. It is also called as
shadowing
According client's requirement, if implementation of base class member function is logically
incomplete then we should override/redefine member function inside derived class.
If name of member function defined in base class & derived class is same and if we call such member
function on object of derived class then preference will be given to derived class member function. In
this case, derived class member function hides implmentation of base class member function. It is
called as shadowing.
In General, to access any member of base class inside member function of derived class, we should
use class name and :: operator.
Applications of scope resolution operator:
To define member function global
To access members of namespace
To access static members
To access members of base class inside member function of derived class.
According client's requirement, if implementation of exisiting class is logically incomplete / partially
complete then to make it complete we should extend that class in other words we should create its
derived class. It means we should use inheritance.
Process of reusing members of parent class inside child class is called as inheritance.
Mode of Inheritance
When we use private/protected/public keyword to control visibility of members of the class inside
class, it is called access specifier. Default access specifier of class is private.
When we use private/protected/public keyword to create derived class then it is called mode of
inheritance.
Example 1:
class Engine{
//TODO
};
class Car{
Engine e; //Association
};
Example 2:
class Engine{
//TODO
};
class Car : private Engine{
//TODO
};
If is-a relationship is exisit between the type then we should use public mode of inheritance.
Example: Tape( CD / DVD / Cassete ) is a Product.
class Product{
//TODO
};
class Tape : public Product{
//TODO
};
Types of inheritance
Interface inheritance
class A{ //Interface
virtual void f1( void ) = 0;
virtual void f2( void ) = 0;
};
class B : public A{ //Interface
virtual void f3( void ) = 0;
}
During inheritance, if parent type and child type is interface then such type of inheritance is
called as interface inheritance.
Single inheritance
Multiple inheritance
Hierarchical inheritace
Multilevel inheritance
Implementation inheritance
class A{ //class
//TODO
};
class B : public A{ //class
//TODO
}
During inheritance, if parent type and child type is class then such type of inheritance is called
as implementation inheritance.
Single inheritance
Multiple inheritance
Hierarchical inheritace
Multilevel inheritance
Single inheritance
Consider below example:
class B is derived from class A
class A{
//TODO
};
class B : public A{
//TODO
};
If single base class is having single derived class then such type of inheritance is called as single
inheritance.
Multiple inheritance
Consider below example:
class C is derived from class A and class B
class A{
//TODO
};
class B{
};
class C : public A, public B{
//TODO
};
If multiple base classes are having single derived class then such type of inheritance is called as
multiple inheritance.
Hierarchical inheritace
Consider below example:
class B and C are derived from class A
class A{
//TODO
};
class B : public A{
//TODO
};
class C : public A{
//TODO
};
If single base class is having multiple derived classes then such type of inheritance is called has
hierarchical inheritance.
Multilevel inheritance
Consider below example:
class B is derived from class A, class C is derived from class B and class D is derived from class
C
class A{
//TODO
};
class B : public A{
//TODO
};
class C : public B{
//TODO
};
class D : public C{
//TODO
};
If single inheritance is having multiple levels then such type of inheritance is called as multilevel
inheritance.
Hybrid inheritance
If we combine any two / more than two types of inheritance then it is called as hybrid inheritance.
Diamond Problem
In case of hybrid inheritance, if we create object of indirect derived class then data members of
indirect base class gets inherited multiple times. Hence it increases size of object.
In case of hybrid inheritance, member functions of indirect base class gets inherited multiple times. In
this case, if we try to call member function of indirect base class on object of indirect derived class
then compiler generate ambiguity error.
If we create object of indirect derived class then constructor and destructor of indirect base gets
called multiple times.
Diamond Problem solution
virtual is a keyword in C++.
To avoid diamond problem, we should declare base class virtual.
class A{
private:
int num1;
int num2;
public:
A( void ){
this->num1 = 10;
this->num2 = 20;
}
A( int num1, int num2 ){
this->num1 = num1;
this->num2 = num2;
}
void showRecord( void ){
cout << "Num1 : " << this->num1 << endl;
cout << "Num2 : " << this->num2 << endl;
}
void printRecord( void ){
cout << "Num1 : " << this->num1 << endl;
cout << "Num2 : " << this->num2 << endl;
}
};
class B : public A{
private:
int num3;
public:
B( void ){
this->num3 = 30;
}
B( int num1, int num2, int num3 ) : A( num1, num2 ){
this->num3 = num3;
}
void printRecord( void ){
A::printRecord( );
cout << "Num3 : " << this->num3 << endl;
}
void displayRecord( void ){
A::showRecord( );
cout << "Num3 : " << this->num3 << endl;
}
};
During inheritance, members( data members + member functions + nested class ) of derived class do
not inherit into base class. Hence using base class object, we can call/access members of base class
only.
During inheritance, members of base class inherit into derived class. Hence using derived class
object, we can access members of base class as well as derived class.
Since members of Base class, inherit into derived class, we can consider Derived class object as a
base class object.
Since derived class object can be considered as base class object, we can assign derived class
object to the Base class Object.
If we assign, Derived class object to the Base class Object then compiler copy state of Base class
portion from derived class object into base class object. This process is called as Object slicing.
During inheritance, members of derived class do not inherit into base class. Hence we can not
consider object of base class as a object of derived class.
Since base class object can not be considered as derived class object, we can not assign base class
object to the derived class object.
Process of converting pointer of Derived class into pointer of Base class is called as upcasting.
int main( void ){
B *ptrDerived = new B( );
ptrDerived->printRecord( ); //OK: B::printRecord( );
//A *ptrBase = ( A* )ptrDerived; //Upcasting: OK
A *ptrBase = ptrDerived; //Upcasting: OK
ptrBase->printRecord( ); //OK: A::printRecord( );
delete ptrDerived;
return 0;
}
process of converting pointer of base class into pointer of derived class is called as downcasting.
Virtual function
In case of upcasting, if we want to call member funtion, depending on type of object rather than type
of pointer then we should declare member function in base class virtual.
If class contains at least one virtual function then such class is called as polymorphic class.
If signature of base class member function and derived class member function is same and if
function in base class is virtual then derived class member function will be considered as virtual.
Process of redefining virtual member function of base, class inside derived class, with same signature
is called as function overriding and virtual function redefined in derived class is called as overrided
function.
For function overriding:
Function must be exist in base class and derived class
Signature of functions ( including return type ) must be same.
Function in base class must be virtual
Definition:
In case of upcasting, A member function, which gets called depending on type of object rather
that type of pointer is called as virtual funtion.
In case of upcasting, A member function of derived class which is deisigned to call using
pointer/reference of base class is called as virtual function.
It means that virtual functions are not designed to call on object / class rather it is designed to call on
base class pointer or base class reference.
Can we declare static member function virtual?
Virtual member function is designed to call on base class pointer / reference.
Static member function is designed to call on class name.
Since static member function is not designed to call on base class pointer / referece, we can not
declare static member function virtual.
Since we can not declare static member function virtual, we can not override it inside derived class.
What is runtime polymorphism:
Process of calling member function derived class on pointer / reference of base class is called as
runtime polymorphism.
Day 13
Early Binding and Late Binding
If call to the function gets resolved at compile time then it is called as early binding. In other words, if
binding between function and object gets resolved at compile time then it is called as early binding.
If call to the function gets resolved at runtime then it is called as late binding. In other words, if
binding between function and object gets resolved at run time then it is called as late binding.
If we call virtual or non virtual function on object then it is considered as early binding. This call
always gets resolved at compile time.
If we call non virtual function on pointer/reference then it is considered as early binding. This call
always gets resolved at compile time.
If we call virtual function on pointer/reference then it is considered as late binding. This call always
gets resolved at run time. Consider below code:
class A{
private:
int num1;
int num2;
public:
A( void ){
this->num1 = 10;
this->num2 = 20;
}
virtual void f1( void ){
cout << "A::f1" << endl;
}
virtual void f2( void ){
cout << "A::f2" << endl;
}
virtual void f3( void ){
cout << "A::f3" << endl;
}
void f4( void ){
cout << "A::f4" << endl;
}
void f5( void ){
cout << "A::f5" << endl;
}
};
class B : public A{
private:
int num3;
public:
B( void ){
this->num3 = 30;
}
virtual void f1( void ){
cout << "B::f1" << endl;
}
void f2( void ){
cout << "B::f2" << endl;
}
void f4( void ){
cout << "B::f4" << endl;
}
virtual void f5( void ){
cout << "B::f5" << endl;
}
virtual void f6( void ){
cout << "B::f6" << endl;
}
};
Members of Base class inherit into Derived class. Hence we can consider Derived class object as
Base class object.
Also Base class pointer can contain address of derived class object.
Members of Derived class do not inherit into Base class. Hence we can not consider Base class
object as Derived class object.
Also Derived class pointer can not contain address of Base class object.
If we want to decide early binding / late binding then we can use below algorithm.
//Check the Data type of caller i.e. Base class or derived class
if( function is not exist in caller data type ){
Compiler error: Function is not a member of caller type.
}else{ //Function is exist in caller data type
//Check whether caller is object, pointer/reference
if( caller is object ){
Early Binding: Member function exist / inherited function of
object type will call.
}else{//Caller is pointer
//Check whether function is virtual / non virtual
if( function is non virtual ){//Call non virtual function on
pointer/reference
Early Binding: Member function exist / inherited function of
pointer type will call.
}else{//Call virtual function on pointer/reference
Late Binding: Member function exist / inherited function of
object type will call.
}
}
}
At the time of creation of V-Table for derived class, compiler simply copy Base class V-Table and
make necessary changes.
Compiler generate v-table per class. It gets generated at compile time.
To store address of virtual function table, compiler implicitly declare pointer as a data meber inside
class. Such pointer is called as virtual function pointer/vf-pointer/v-ptr.
In short, a pointer which contain address of virtual function table is called as v-ptr.
Consider V-Table and V-Ptr for the class A.
Consider V-Table and V-Ptr for the class B.
class Base{
private:
int *ptr;
public:
Base( void ){
this->ptr = new int[ 3 ];
}
virtual ~Base( void ){
delete[] this->ptr;
}
};
class Derived : public Base{
private:
int *ptr;
public:
Derived( void ){
this->ptr = new int[ 5 ];
}
~Derived( void ){
delete[] this->ptr;
}
};
int main( void ){
Base *ptr = new Derived( );
//TODO
delete ptr;
return 0;
}
#include<iostream>
#include<typeinfo>
using namespace std;
int main( void ){
int number;
const type_info &type = typeid( number );
string typeName = type.name( );
cout << "Type Name : " << typeName << endl;
return 0;
}
namespace std {
class type_info{
public:
const char* name() const noexcept;
bool operator==(const type_info& rhs) const noexcept;
bool operator!=(const type_info& rhs) const noexcept;
virtual ~type_info();
private:
type_info(const type_info& rhs);
type_info& operator=(const type_info& rhs);
};
}
Process of getting type of object at runtime is called as Runtime Type Identification / Information.
In case of upcasting, if we want to find out true type of object then we should use RTTI.
Consider below code:
#include<iostream>
#include<typeinfo>
using namespace std;
class Base{
int num1;
public:
Base( void ){
this->num1 = 10;
}
void print( void ){
cout << "Num1 : " << this->num1 << endl;
}
};
class Derived : public Base{
int num2;
public:
Derived( void ){
this->num2 = 20;
}
void print( void ){
Base::print( );
cout << "Num2 : " << this->num2 << endl;
}
};
int main( void ){
Base *ptrBase = new Derived( ); //Upcasting
cout << typeid( ptrBase ).name( ) << endl; //P4Base
cout << typeid( *ptrBase ).name( ) << endl; //4Base
return 0;
}
int main4( void ){
Derived *ptrDerived = new Derived( );
cout << typeid( ptrDerived ).name( ) << endl; //P7Derived
cout << typeid( *ptrDerived ).name( ) << endl; //7Derived
return 0;
}
int main3( void ){
Derived derived;
cout << typeid( derived ).name( ) << endl; //7Derived
return 0;
}
In case of upcasting, using RTTI, to get true type of object, Base class must be polymorphic.
class Base{
int num1;
public:
Base( void ){
this->num1 = 10;
}
virtual void print( void ){
cout << "Num1 : " << this->num1 << endl;
}
};
class Derived : public Base{
int num2;
public:
Derived( void ){
this->num2 = 20;
}
void print( void ){
Base::print( );
cout << "Num2 : " << this->num2 << endl;
}
};
int main( void ){
Base *ptrBase = new Derived( ); //Upcasting
cout << typeid( ptrBase ).name( ) << endl; //P4Base
cout << typeid( *ptrBase ).name( ) << endl; //7Derived
return 0;
}
Using Null pointer, if we try to find out true type of object then typeid operator throws bad_typeid
exception.
#include<iostream>
using namespace std;
class Complex{
private:
int real;
int imag;
public:
Complex( void ){
this->real = 10;
this->imag = 20;
}
friend ostream& operator<<( ostream &cout, Complex &other ){
cout << "Real Number : " << other.real << endl;
cout << "Imag Number : " << other.imag << endl;
return cout;
}
};
int main( void ){
Complex c1;
cout << c1 << endl;
//int *ptr = (int*)(&c1); //C-Style
int *ptr = reinterpret_cast<int*>( &c1 ); //C++ Style
*ptr = 50;
ptr = ptr + 1;
*ptr = 60;
cout << c1 << endl;
return 0;
}
const_cast operator
If we want to convert pointer to constant object into pointer to non constant object or reference to
constant object into reference to non constant object then we should use const_cast operator.
#include<iostream>
using namespace std;
class Test{
int number;
public:
//Test *const this
Test( void ){
this->number = 10;
}
//Test *const this
void showRecord( void ){
cout << "Number : "<<this->number << endl;
}
In case of non polymorphic type, if we want to do downcasting the we should use static_cast
operator.
static_cast operator do not check whether type consversion is valid or invalid. it only checks
inheritance relationship at compile time.
dynamic_cast operator
In case of polymorphic type, if we want to do downcasting the we should use dynamic_cast operator.
If we want to check whether, type conversion is valid or invalid then we should use dynamic_cast
operator.
dynamic_cast operator checks inheritance relationship at runtime.
In case of pointer, if dynamic_cast operator fail to do conversion then it returns NULL.
In case of reference, if dynamic_cast operator fail to do conversion then it throws bad_cast excetion.
int main( void ){
Base *ptrBase = new Derived( ); //OK: Upcasting
ptrBase->setNum1( 10 ); //OK
ptrBase->setNum2( 20 ); //OK
Derived *ptrDerived = dynamic_cast< Derived*>( ptrBase );
//Downcasting: C++ -Style
if( ptrDerived != NULL ){
ptrDerived->setNum3( 30 );
ptrDerived->Base::print( );
ptrDerived->Derived::print( );
delete ptrBase;
}
return 0;
}
class Shape{
protected:
float area;
public:
Shape( void ){
this->area = 0;
}
virtual void acceptRecord( void ) = 0; //Pure Virtual Function
virtual void calculateArea( void ) = 0; //Pure Virtual Function
void printRecord( void ){
cout << "Area : " << this->area << endl;
}
virtual ~Shape( void ){ }
};
We can not provide body to the pure virtual function. Hence it is also called as abstract method.
If class contains at least one pure virtual function then such class is called as abstract class.
If class contains all pure virtual function then such class is called as pure abstract class / interface.
We can not create object of abstract class and interface but we can create pointer / reference of it.
It is mandatory to override pure virtual function in derived class otherwise derived class can be
considered as abstract.
If we create object of derived class then constructor and destructor of base class gets called. Hence
abstract class can contain constructor as well as destructor.
Virtual Base Class Pointer