0% found this document useful (0 votes)
5 views269 pages

C++ Inheritance and Object-Oriented Concepts

The document provides an overview of Object-Oriented Programming (OOP) concepts, specifically focusing on inheritance in C++. It explains the relationship between base and derived classes, access specifiers, memory allocation, constructors, destructors, and copy constructors. Additionally, it discusses the implications of public inheritance and the 'IS A' relationship, along with examples in C++ to illustrate these concepts.

Uploaded by

Muhammad Ammar
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)
5 views269 pages

C++ Inheritance and Object-Oriented Concepts

The document provides an overview of Object-Oriented Programming (OOP) concepts, specifically focusing on inheritance in C++. It explains the relationship between base and derived classes, access specifiers, memory allocation, constructors, destructors, and copy constructors. Additionally, it discusses the implications of public inheritance and the 'IS A' relationship, along with examples in C++ to illustrate these concepts.

Uploaded by

Muhammad Ammar
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

Object-Oriented Programming

(OOP)
Syed Saqlain Hassan, PhD
Inheritance in Classes
► If a class B inherits from class A, then B contains
all the characteristics (information structure and
behavior) of class A
► The parent class is called base class and the
child class is called derived class
► Besides inherited characteristics, derived class
may have its own unique characteristics
UML Notation

Parent Class Base Class

Child Class Derived Class


Inheritance in C++

► There are three types of inheritance in C++

▪ Public
▪ Private
▪ Protected
“IS A” Relationship
► ISA relationship is modeled with the help of
public inheritance

► Syntax
class ChildClass
: public BaseClass{
...
};
Example
class Person{
...
};
class Student: public Person{
...
};
Accessing Members
► Public members of base class become
public member of derived class
► Private members of base class are not
accessible from outside of base class,
even in the derived class (Information
Hiding)
Example
class Person{
char *name;
int age;
...
public:
const char *GetName() const;
int GetAge() const;
...
};
Example
class Student: public Person{
int semester;
int rollNo;
...
public:
int GetSemester() const;
int GetRollNo() const;
void Print() const;
...
};
Example

void Student::Print()
{ ERROR
cout << name << “ is in” << “
semester ” << semester;
}
Example

void Student::Print()
{
cout << GetName()
<< “ is in semester ”
<< semester;
}
Example
int main(){
Student stdt;

[Link] = 0;//error
[Link] = NULL; //error
cout << [Link]();
cout << [Link]();
return 0;
}
Allocation in Memory
► Theobject of derived class is represented
in memory as follows

base member1
base member2 Data members of
... base class
derived member1 Data members of
derived member2 derived class
...
Allocation in Memory
► Every
object of derived class has an
anonymous object of base class
Constructors
► The anonymous object of base class must
be initialized using constructor of base
class
► When a derived class object is created the
constructor of base class is executed
before the constructor of derived class
Constructors

base member1 Base class constructor


base member2 initializes the anonymous
... object
derived member1 Derived class constructor
derived member2 initializes the derived
... class object
Example
class Parent{
public:
Parent(){ cout <<
“Parent Constructor...”;}
};
class Child : public Parent{
public:
Child(){ cout <<
“Child Constructor...”;}
};
Example
int main(){
Child cobj;
return 0;
}

Output:
Parent Constructor...
Child Constructor...
Constructor
► If default constructor of base class does
not exist then the compiler will try to
generate a default constructor for base
class and execute it before executing
constructor of derived class
Constructor
► Ifthe user has given only an overloaded
constructor for base class, the compiler
will not generate default constructor for
base class
Example
class Parent{
public:
Parent(int i){}
};
class Child : public Parent{
public:
Child(){}
} Child_Object; //ERROR
Base Class Initializer
► C++ has provided a mechanism to
explicitly call a constructor of base class
from derived class

► Thesyntax is similar to member initializer


and is referred as base-class initialization
Example
class Parent{
public:
Parent(int i){…};
};
class Child : public Parent{
public:
Child(int i): Parent(i)
{…}
};
Example
class Parent{
public:
Parent(){cout <<
“Parent Constructor...”;}
...
};
class Child : public Parent{
public:
Child():Parent()
{cout << “Child Constructor...”;}
...
};
Base Class Initializer
► User
can provide base class initializer and
member initializer simultaneously
Example
class Parent{
public:
Parent(){…}
};
class Child : public Parent{
int member;
public:
Child():member(0), Parent()
{…}
};
Base Class Initializer
► The base class initializer can be written
after member initializer for derived class
► The base class constructor is executed
before the initialization of data members
of derived class.
Initializing Members
► Derivedclass can only initialize members
of base class using overloaded
constructors
▪ Derived class can not initialize the public data
member of base class using member
initialization list
Example
class Person{
public:
int age;
char *name;
...
public:
Person();
};
Example
class Student: public Person{
private:
int semester;
...
public:
Student(int a):age(a)
{ //error
}
};
Reason
► It will be an assignment not an initialization
Destructors

► Destructors are called in reverse order of


constructor called
► Derived class destructor is called before the
base class destructor is called
Example
class Parent{
public:
Parent(){cout <<“Parent Constructor”;}
~Parent(){cout<<“Parent Destructor”;}
};

class Child : public Parent{


public:
Child(){cout << “Child Constructor”;}
~Child(){cout << “Child Destructo”;}
};
Example
Output:

Parent Constructor
Child Constructor
Child Destructor
Parent Destructor
Object-Oriented Programming
(OOP)
Syed Saqlain Hassan, PhD
Date Class
class Date{
int day, month, year;
static Date defaultDate;
public:
void SetDay(int aDay);
int GetDay() const;
void AddDay(int x);

static void SetDefaultDate(
int aDay,int aMonth, int aYear);
Date Class
...
private:
bool IsLeapYear();
};

int main(){
Date aDate;
[Link](); //Error
return 0;
}
Creating SpecialDate Class

Date Special Date

AddSpecialYear
Special Date ...
Creating SpecialDate Class
class SpecialDate: public Date{

public:
void AddSpecialYear(int i){
...
if(day == 29 && month == 2
&& !IsLeapyear(year+i)){ //ERROR!
...
}
}
};
Modify Access Specifier
• We can modify access specifier
“IsLeapYear” from private to
public
Modified Date Class
class Date{
public:
...
bool IsLeapYear();
};
Modified AddSpecialYear
void SpecialDate :: AddSpecialYear
(int i){
...
if(day == 29 && month == 2
&& !IsLeapyear(year+i)){
...
}
}
Protected members
• Protected members can not be
accessed outside the class
• Protected members of base class
become protected member of
derived class in Public inheritance
Modified Date Class
class Date{

protected:
bool IsLeapYear();
};

int main(){
Date aDate;
[Link](); //Error
return 0;
}
Modified AddSpecialYear
void SpecialDate :: AddSpecialYear
(int i){
...
if(day == 29 && month == 2
&& !IsLeapyear(year+i)){
...
}
}
Disadvantages
• Breaks encapsulation
–The protected member is part of
base class’s implementation as well
as derived class’s implementation
“IS A” Relationship
• Public inheritance models the “IS
A” relationship
• Derived object IS A kind of base
object
Example
class Person {
char * name;
public: ...
const char * GetName();
};
class Student: public Person{
int rollNo;
public: ...
int GetRollNo();
};
Example
int main()
{
Student sobj;
cout << [Link]();
cout << [Link]();
return 0;
}
“IS A” Relationship
• The base class pointer can point
towards an object of derived class
Example
int main(){
Person * pPtr = 0;
Student s;
pPtr = &s;
cout << pPtr->GetName();

return 0;
}
Example
pPtr = &s;
s
pPtr base member1
base member2
...
derived member1
derived member2
...
Example
int main(){
Person * pPtr = 0;
Student s;
pPtr = &s;
//Error
cout << pPtr->GetRollNo();
return 0;
}
Static Type
• The type that is used to declare a
reference or pointer is called its
static type
–The static type of pPtr is Person
–The static type of s is Student
Member Access
• The access to members is
determined by static type
• The static type of pPtr is Person

• Following call is erroneous


pPtr->GetRollNo();
“IS A” Relationship
• We can use a reference of
derived object where the
reference of base object is
required
Example
int main(){
Person p;
Student s;
Person & refp = s;
cout << [Link]();
cout << [Link](); //Error
return 0;
}
Example
void Play(const Person& p){
cout << [Link]()
<< “ is playing”;
}
void Study(const Student& s){
cout << [Link]()
<< “ is Studying”;
}
Example
int main(){
Person p;
Student s;
Play(p);
Play(s);
return 0;
}
Object-Oriented Programming
(OOP)
Syed Saqlain Hassan, PhD
Example
class Person{
char * name;
public:
Person(char * = NULL);
const char * GetName() const;
~Person();
};
Example
class Student: public Person{
char* major;
public:
Student(char *, char *);
void Print() const;
~Student();
};
Example
Student::Student(char *_name, char
*_maj) : Person(_name), major(NULL)
{
if (_maj != NULL) {
major = new char [strlen(_maj)+1];
strcpy(major,_maj);
}
}
Example
void Student::Print() const{
cout << “Name: ”<< GetName()
<<endl;
cout << “Major: “ << major
<< endl;
}
Example
int main(){
Student sobj1(“Ali”, “Computer Science”);
{
Student sobj2 = sobj1;
// Student sobj2(sobj1);
[Link]();
}
return 0;
}
Example

• The output is as follows:

Name: Ali
Major: Computer Science
Copy Constructor
• Compiler generates copy constructor
for base and derived classes, if
needed
• Derived class Copy constructor is
invoked which in turn calls the Copy
constructor of the base class
• The base part is copied first and then
the derived part
Shallow Copy
A
sobj1 L sobj2
name I name
major major
... C ...
O
M
...
Example
Person::Person(const Person& rhs){
// Code for deep copy
}

int main(){
Student sobj1(“Ali”, “Computer Science”);
Student sobj2 = sobj1;
[Link]();
return 0;
}
Example

• The output is as follows:

Name: Ali
Major: Computer Science
Copy Constructor
• Compiler generates copy
constructor for derived class, calls
the copy constructor of the base
class and then performs the
shallow copy of the derived
class’s data members
Shallow Copy
A A
sobj1 L L sobj2
name I I name
major major
... C ...
O
M
...
Example
Person::Person(const Person& rhs)
{
// Code for deep copy
}
Student::Student
(const Student& rhs) {
// Code for deep copy
}
Example
int main(){
Student sobj1(“Ali”,
“Computer Science”);
Student sobj2 = sobj1;
[Link]();
return 0;
}
Copy Constructor
• The output will be as follows:

Name:
Major: Computer Science

• Name of sobj2 was not copied


from sobj1
Copy
A
sobj1 L sobj2
name I name
major major
... C C ...
O O
M M
... ...
Modified Default Constructor
Person::Person(char * aName){
if(aName == NULL)
cout << “Person Constructor”;
...
}
int main(){
Student s (“Ali”,“Computer Science”);

}
Copy Constructor
• The output of previous code will
be as follows:

Person Constructor
Name:
Major: Computer Science
Copy Constructor
• Programmer must explicitly call
the base class copy constructor
from the copy constructor of
derived class
Example
Person::Person(const Person&
prhs) {
// Code for deep copy
}

Student::Student(const Student
&srhs) :Person(srhs) {
// Code for deep copy
}
Example
• main function shown previously
will give following output

Name: Ali
Major: Computer Science
Copy
A A
sobj1 L L sobj2
name I I name
major major
... C C ...
O O
M M
... ...
Copy Constructors
3 Person::Person(const Person &rhs) :
4 name(NULL) {
5 //code for deep copy
}
1 Student::Student(const Student & rhs) :
6 major(NULL),
2 Person(rhs){
7 //code for deep copy
}
Example
int main()
{
Student sobj1, sboj2(“Ali”, “CS”);
sobj1 = sobj2;
return 0;
}
Assignment Operator
• Compiler generates copy assignment
operator for base and derived
classes, if needed
• Derived class copy assignment
operator is invoked which in turn calls
the assignment operator of the base
class
• The base part is assigned first and
then the derived part
Assignment Operator
• Programmer has to call operator
of base class, if he is writing
assignment operator of derived
class
Example
class Person{
public:
Person & operator =
(const Person & rhs){
cout << “Person Assignment”;
// Code for deep copy assignment
}
};
Example
class Student: Public Person{
public:
Student & operator = (const Student
& rhs){
cout<< “Student Assignment”;
// Code for deep copy assignment
}
};
Example
int main()
{
Student sobj1, sboj2(“Ali”, “CS”);
sobj1 = sobj2;
return 0;
}
Example
• The assignment operator of base
class is not called
• Output

Student Assignment
Assignment Operator
• There are two ways of writing
assignment operator in derived
class
–Calling assignment operator of
base class explicitly
–Calling assignment operator of
base class implicitly
Calling Base Class Member
Function
• Base class functions can be explicitly
called with reference to base class itself

//const char* Person::GetName() {...};


void Student::Print()
{
cout << GetName();
cout << Person::GetName();
}
Explicitly Calling operator =
Person & Person::operator =
(const Person & prhs);

Student & Student ::operator =


(const Student & srhs){
Person::operator = (srhs);

return *this;
}
Implicitly Calling operator =
Student & Student ::operator =
(const Student & srhs) {
static_cast<Person &>*this=srhs;
// Person(*this) = srhs;
// (Person)*this = srhs;

}
Object-Oriented Programming
(OOP)
Syed Saqlain Hassan, PhD
Overriding Member Functions of
Base Class
• Derived class can override the
member functions of its base
class
• To override a function the derived
class simply provides a function
with the same signature as that of
its base class
Overriding
Parent
...
Func1

Child
...
Func1
Overriding
class Parent {
public:
void Func1();
void Func1(int);
};

class Child: public Parent {


public:
void Func1();
};
Overloading vs. Overriding
• Overloading is done within the
scope of one class
• Overriding is done in scope of
parent and child
• Overriding within the scope of
single class is error due to
duplicate declaration
Overriding
class Parent {
public:
void Func1();
void Func1(); //Error
};
Overriding Member Functions of
Base Class

• Derive class can override


member function of base class
such that the working of function
is totally changed
Example
class Person{
public:
void Walk();
};
class ParalyzedPerson: public Person{
public:
void Walk();
};
Overriding Member Functions of
Base Class
• Derive class can override
member function of base class
such that the working of function
is similar to former
implementation
Example
class Person{
char *name;
public:
Person(char *=NULL);
const char *GetName() const;
void Print(){
cout << “Name: ” << name
<< endl;
}
};
Example
class Student : public Person{
char * major;
public:
Student(char * aName, char* aMajor);

void Print(){
cout <<“Name: ”<< GetName()<<endl
<< “Major:” << major<< endl;
}
...
};
Example
int main(){
Student a(“Ahmad”,
“Computer Science”);
[Link]();
return 0;
}
Output
Output:

Name: Ahmed
Major: Computer Science
Overriding Member Functions of
Base Class

• Derive class can override


member function of base class
such that the working of function
is based on former
implementation
Example
class Student : public Person{
char * major;
public:
Student(char * aName, char* m);

void Print(){
Print();//Print of Person
cout<<“Major:” << major <<endl;
}
...
};
Example
int main(){
Student a(“Ahmad”,
“Computer Science”);
[Link]();
return 0;
}
Output
• There will be no output as the
compiler will call the print of the
child class from print of child class
recursively
• There is no ending condition
Example
class Student : public Person{
char * major;
public:
Student(char * aName, char* m);

void Print(){
Person::Print();
cout<<“Major:” << major <<endl;
}
...
};
Example
int main(){
Student a(“Ahmad”,
“Computer Science”);
[Link]();
return 0;
}
Output
Output:

Name: Ahmed
Major: Computer Science
Overriding Member Functions of
Base Class

• The pointer must be used with


care when working with
overridden member functions
Example
int main(){
Student a(“Ahmad”, “Computer
Scuence”);
Student *sPtr = &a;
sPtr->Print();

Person *pPtr = sPtr;


pPtr->Print();
return 0;
}
Example
Output:

Name: Ahmed
Major: Computer Science

Name: Ahmed
Overriding Member Functions of
Base Class
• The member function is called
according to static type
• The static type of pPtr is Person
• The static type of sPtr is Student
Hierarchy of Inheritance
• We represent the classes
involved in inheritance relation in
tree like hierarchy
Example

GrandParent

Parent1 Parent2

Child1 Child2
Direct Base Class
• A direct base class is explicitly listed
in a derived class's header with a
colon (:)

class Child1:public Parent1


...
Indirect Base Class
• An indirect base class is not explicitly
listed in a derived class's header with
a colon (:)
• It is inherited from two or more levels
up the hierarchy of inheritance

class GrandParent{};
class Parent1:
public GrandParent {};
class Child1:public Parent1{};
Object-Oriented Programming
(OOP)
Syed Saqlain Hassan, PhD
Hierarchy of Inheritance
• We represent the classes
involved in inheritance relation in
tree like hierarchy
Example

GrandParent

Parent1 Parent2

Child1 Child2
Direct Base Class
• A direct base class is explicitly listed
in a derived class's header with a
colon (:)

class Child1:public Parent1


...
Indirect Base Class
• An indirect base class is not explicitly
listed in a derived class's header with
a colon (:)
• It is inherited from two or more levels
up the hierarchy of inheritance

class GrandParent{};
class Parent1:
public GrandParent {};
class Child1:public Parent1{};
Base Initialization
• The child can only perform the
initialization of direct base class
through base class initialization
list
• The child can not perform the
initialization of an indirect base
class through base class
initialization list
Example
class GrandParent{
int gpData;
public:
GrandParent() : gpData(0){...}
GrandParent(int i) : gpData(i){...}
void Print() const;
};
Example
class Parent1: public GrandParent{
int pData;
public:
Parent1() : GrandParent(),
pData(0) {…}
};
Example
class Child1 : public Parent1 {
public:
Child1() : Parent1() {...}
Child1(int i) : GrandParent (i)
//Error
{...}
void Print() const;
};
Overriding
• Child class can override the
function of GrandParent class
Example
GrandParent
Print()

Parent1

Child1
Print()
Example
void GrandParent::Print() {
cout << “GrandParent::Print”
<< endl;
}

void Child1::Print() {
cout << “Child1::Print” << endl;
}
Example
int main(){
Child1 obj;
[Link]();
obj.Parent1::Print();
[Link]::Print();
return 0;
}
Output
• Output is as follows

Child1::Print
GrandParent::Print
GrandParent::Print
Types of Inheritance
• There are three types of
inheritance
–Public
–Protected
–Private
• Use keyword public, private or
protected to specify the type of
inheritance
Public Inheritance
class Child: public Parent {…};

Member access in
Base Class Derived Class
Public Public
Protected Protected
Private Hidden
Protected Inheritance
class Child: protected Parent {…};

Member access in
Base Class Derived Class
Public Protected
Protected Protected
Private Hidden
Private Inheritance
class Child: private Parent {…};

Member access in
Base Class Derived Class
Public Private
Protected Private
Private Hidden
Private Inheritance
• If the user does not specifies the
type of inheritance then the
default type is private inheritance

class Child: private Parent {…}


is equivalent to
class Child: Parent {…}
Private Inheritance
• We use private inheritance when
we want to reuse code of some
class
• Private Inheritance is used to
model “Implemented in terms of”
relationship
Example
class Collection {
...
public:
void AddElement(int);
bool SearchElement(int);
bool SearchElementAgain(int);
bool DeleteElement(int);
};
Example
• If element is not found in the
Collection the function
SearchElement will return false
• SearchElementAgain finds the
second instance of element in the
collection
Class Set
class Set: private Collection {
private:
...
public:
void AddMember(int);
bool IsMember(int);
bool DeleteMember(int);
};
Class Set
void Set::AddMember(int i){
if (! IsMember(i) )
AddElement(i);
}
bool Set::IsMember(int i){
return SearchElement(i);
}
Object-Oriented Programming
(OOP)
Syed Saqlain Hassan, PhD
Class Collection
class Collection {
...
public:
void AddElement(int);
bool SearchElement(int);
bool SearchElementAgain(int);
bool DeleteElement(int);
};
Class Set
class Set: private Collection {
private:
...
public:
void AddMember(int);
bool IsMember(int);
bool DeleteMember(int);
};
Specialization (Restriction)
• the derived class is behaviourally
incompatible with the base class

• Behaviourally incompatible
means that base class can’t
always be replaced by the derived
class
Specialization (Restriction)
• Specialization (Restriction) can be
implemented using private and
protected inheritance
Example – Specialization
(Restriction)
Person
age : [0..125] age = a
setAge( a )

If age < 18 then


Adult error
age : [18..125] else
setAge( a ) age = a
Example
class Person{

protected:
int age;
public:
bool SetAge(int _age){
if (_age >=0 && _age <= 125) {
age = _age;
return true;
}
return false;
}
};
Example
class Adult : private Person {
public:
bool SetAge(int _age){
if (_age >=18 && _age <= 125) {
age = _age;
return true;
}
return false;
}
};
Private Inheritance
• Only member functions and friend
functions of a derived class can
convert pointer or reference of
derived object to that of parent
object
Example
class Parent{
};
class Child : private Parent{
};

int main(){
Child cobj;
Parent *pptr = & cobj; //Error
return 0;
}
Example
void DoSomething(const Parent &);

Child::Child(){
Parent & pPtr =
static_cast<Parent &>(*this);
DoSomething(pPtr);
// DoSomething(*this);
}
Private Inheritance
• The child class object has an
anonymous object of parent class
object
• The default constructor and copy
constructor of parent class are
called when needed
Example
class Parent{
public:
Parent(){
cout << “Parent Constructor”;
}

Parent(const Parent & prhs){


cout << “Parent Copy Constructor”;
}
};
Example
class Child: private Parent{
public:
Child(){
cout << “Child Constructor”;
}

Child(const Child & crhs)


:Parent(crhs){
cout << “Child Copy Constructor”;
}
};
Example
int main() {
Child cobj1;
Child cobj2 = cobj1;
//Child cobj2(cobj1);
return 0;
}
Example
• Output:

Parent Constructor
Child Constructor
Parent Copy Constructor
Child Copy Constructor
Private Inheritance
• The base class that is more then
one level down the hierarchy
cannot access the member
function of parent class, if we are
using private inheritance
Class Hierarchy
class GrandParent{
public :
void DoSomething();
};

class Parent: private GrandParent{


void SomeFunction(){
DoSomething();
}
};
Example
class Child: private Parent
{
public:
Child() {
DoSomething(); //Error
}
};
Private Inheritance
• The base class that is more then
one level down the hierarchy
cannot convert the pointer or
reference to child object to that of
parent, if we are using private
inheritance
Class Hierarchy
void DoSomething(GrandParent&);
class GrandParent{
};
class Parent: private GrandParent{
public:
Parent() {DoSomething(*this);}
};
Example
class Child: private Parent {
public:
Child()
{
DoSomething(*this); //Error
}
};
Protected Inheritance
• Use protected inheritance if you
want to build class hierarchy
using “implemented in terms of”
Protected Inheritance
• If B is a protected base and D is
derived class then public and
protected members of B can be
used by member functions and
friends of classes derived from D
Class Hierarchy
class GrandParent{
public :
void DoSomething();
};

class Parent: protected GrandParent{


void SomeFunction(){
DoSomething();
}
};
Example
class Child: protected Parent
{
public:
Child()
{
DoSomething();
}
};
Protected Inheritance
• If B is a protected base and D is
derived class then only friends
and members of D and friends
and members of class derived
from D can convert D* to B* or D&
to B&
Class Hierarchy
void DoSomething(GrandParent&);

class GrandParent{
};

class Parent: protected


GrandParent{
};
Example
class Child: protected Parent {
public:
Child()
{
DoSomething(*this);
}
};
Object-Oriented Programming
(OOP)
Syed Saqlain Hassan, PhD
Problem Statement

• Develop a function that can draw different


types of geometric shapes from an array
Shape Hierarchy

Shape
draw
calcArea

Line Circle Triangle


draw draw draw
calcArea calcArea calcArea
Shape Hierarchy
class Shape {

protected:
char _type;
public:
Shape() { }
void draw(){ cout << “Shape\n”; }
int calcArea() { return 0; }
char getType() { return _type; }
}
… Shape Hierarchy
class Line : public Shape {

public:
Line(Point p1, Point p2) {

}
void draw(){ cout << “Line\n”; }
}
… Shape Hierarchy
class Circle : public Shape {

public:
Circle(Point center, double radius)
{

}
void draw(){ cout << “Circle\n”; }
int calcArea() { … }
}
… Shape Hierarchy
class Triangle : public Shape {

public:
Triangle(Line l1, Line l2,
double angle)
{ … }
void draw(){ cout << “Triangle\n”; }
int calcArea() { … }
}
Drawing a Scene
int main() {
Shape* _shape[ 10 ];
Point p1(0, 0), p2(10, 10);
shape[1] = new Line(p1, p2);
shape[2] = new Circle(p1, 15);

void drawShapes( shape, 10 );
return 0;
}
Function drawShapes()

void drawShapes(Shape* _shape[],


int size) {
for (int i = 0; i < size; i++)
{
_shape[i]->draw();
}
}
Sample Output

Shape
Shape
Shape
Shape

Function drawShapes()
void drawShapes(
Shape* _shape[], int size) {
for (int i = 0; i < size; i++) {
// Determine object type with
// switch & accordingly call
// draw() method
}
}
Required Switch Logic
switch ( _shape[i]->getType() )
{
case ‘L’:
static_cast<Line*>(_shape[i])->draw();
break;
case ‘C’:
static_cast<Circle*>(_shape[i])
->draw();
break;

}
Equivalent If Logic

if ( _shape[i]->getType() == ‘L’ )
static_cast<Line*>(_shape[i])->draw();
else if ( _shape[i]->getType() == ‘C’ )
static_cast<Circle*>(_shape[i])->draw();

Sample Output

Line
Circle
Triangle
Circle

Problems with Switch
Statement
…Delocalized Code

• Consider a function that prints area of


each shape from an input array
Function printArea
void printArea(
Shape* _shape[], int size) {
for (int i = 0; i < size; i++) {
// Print shape name.
// Determine object type with
// switch & accordingly call
// calcArea() method.
}
}
Required Switch Logic
switch ( _shape[i]->getType() )
{
case ‘L’:
static_cast<Line*>(_shape[i])
->calcArea(); break;
case ‘C’:
static_cast<Circle*>(_shape[i])
->calcArea(); break;

}
…Delocalized Code

• The above switch logic is same as was in


function drawArray()

• Further we may need to draw shapes or


calculate area at more than one places in
code
Other Problems

• Programmer may forget a check

• May forget to test all the possible cases

• Hard to maintain
Solution?

• To avoid switch, we need a mechanism


that can select the message target
automatically!
Polymorphism Revisited
• In OO model, polymorphism means that
different objects can behave in different
ways for the same message (stimulus)

• Consequently, sender of a message does


not need to know the exact class of
receiver
Virtual Functions
• Target of a virtual function call is
determined at run-time
• In C++, we declare a function virtual by
preceding the function header with
keyword “virtual”

class Shape {

virtual void draw();
}
Shape Hierarchy

Shape
draw
calcArea

Line Circle Triangle


draw draw draw
calcArea calcArea calcArea
…Shape Hierarchy Revisited
No type field
class Shape {

virtual void draw();
virtual int calcArea();
}
class Line : public Shape {

virtual void draw();
}
… Shape Hierarchy Revisited
class Circle : public Shape {

virtual void draw();
virtual int calcArea();
}
class Triangle : public Shape {

virtual void draw();
virtual int calcArea();
}
Function drawShapes()

void drawShapes(Shape* _shape[],


int size) {
for (int i = 0; i < size; i++) {
_shape[i]->draw();
}
}
Sample Output

Line
Circle
Triangle
Circle

Function printArea
void printArea(Shape* _shape[],
int size) {
for (int i = 0; i < size; i++) {
// Print shape name
cout<< _shape[i]
->calcArea();
cout << endl;
}
}
Static vs Dynamic Binding

• Static binding means that target function


for a call is selected at compile time

• Dynamic binding means that target


function for a call is selected at run time
Static vs Dynamic Binding
Line _line;
_line.draw(); // Always Line::draw
// called
Shape* _shape = new Line();
_shape->draw(); // Shape::draw called
// if draw() is not virtual

Shape* _shape = new Line();


_shape->draw(); // Line::draw called
// if draw() is virtual
Object-Oriented Programming
(OOP)
Syed Saqlain Hassan, PhD
Abstract Class

Shape
draw
calcArea

Line Circle Triangle


draw draw draw
calcArea calcArea calcArea
Abstract Class

• Implements an abstract concept

• Cannot be instantiated

• Used for inheriting interface and/or


implementation
Concrete Class

• Implements a concrete concept

• Can be instantiated

• May inherit from an abstract class or


another concrete class
Abstract Classes in C++

• In C++, we can make a class abstract by


making its function(s) pure virtual

• Conversely, a class with no pure virtual


function is a concrete class
Pure Virtual Functions function
• A pure virtual represents an abstract
behavior and therefore may not have its
implementation (body)

• A function is declared pure virtual by


following its header with “= 0”

virtual void draw() = 0;


… Pure Virtual Functions
• A class having pure virtual function(s)
becomes abstract

class Shape {

public:
virtual void draw() = 0;
}

Shape s; // Error!
… Pure Virtual Functions

• A derived class of an abstract class


remains abstract until it provides
implementation for all pure virtual
functions
Shape Hierarchy
Shape
draw = 0

Line Circle Quadrilateral


draw draw

Rectangle
draw
… Pure Virtual Functions

class Quadrilateral : public Shape {



// No overriding draw() method
}

Quadrilateral q; // Error!
… Pure Virtual Functions
class Rectangle:public Quadrilateral{

public:
// void draw()
virtual void draw() {
… // function body
}
}

Rectangle r; // OK
Virtual Destructors
class Shape {

public:
~Shape() {
cout << “Shape destructor
called\n”;
}
}
…Virtual Destructors
class Quadrilateral : public Shape {

public:
~Quadrilateral() {
cout << “Quadrilateral destructor
called\n”;
}
}
…Virtual Destructors
class Rectangle : public
Quadrilateral {

public:
~Rectangle() {
cout << “Rectangle destructor
called\n”;
}
}
…Virtual Destructors

• When delete operator is applied to a base


class pointer, base class destructor is
called regardless of the object type
…Virtual Destructors
int main() {
Shape* pShape = new Rectangle();
delete pShape;
return 0;
}

• Output
Shape destructor called
Result

pShape pShape

Shape Part
Quad Part Quad Part
Rect Part Rect Part

Before After
Virtual Destructors
• Make the base class destructor virtual

class Shape {

public:
virtual ~Shape() {
cout << “Shape destructor
called\n”; }
}
…Virtual Destructors
class Quadrilateral : public Shape {

public:
virtual ~Quadrilateral() {
cout << “Quadrilateral destructor
called\n”;
}
}
…Virtual Destructors
class Rectangle : public
Quadrilateral {

public:
virtual ~Rectangle() {
cout << “Rectangle destructor
called\n”;
}
}
…Virtual Destructors

• Now base class destructor will run after


the derived class destructor
…Virtual Destructors
int main() {
Shape* pShape = new Recrangle();
delete pShape;
return 0;
}

• Output
Rectangle destructor called
Quadilateral destructor called
Shape destructor called
Result

pShape pShape

Shape Part
Quad Part
Rect Part
Before After
Virtual Functions – Usage

• Inherit interface and implementation

• Just inherit interface (Pure Virtual)


Inherit interface and
implementation
Shape
draw = 0
calcArea

Line Circle Triangle


draw draw draw
calcArea calcArea
…Inherit interface and
implementation
class Shape {

virtual void draw() = 0;

virtual float calcArea() {


return 0;
}
}
…Inherit interface and
implementation

• Each derived class of Shape inherits


default implementation of calcArea()

• Some may override this, such as Circle


and Triangle

• Others may not, such as Point and Line


…Inherit interface and
implementation

• Each derived class of Shape inherits


interface (prototype) of draw()

• Each concrete derived class has to


provide body of draw() by overriding it
V Table

• Compiler builds a virtual function table


(vTable) for each class having virtual
functions

• A vTable contains a pointer for each virtual


function
Example – V Table

int main() {
Point p1( 10, 10 ), p2( 30, 30 );
Shape* pShape;

pShape = new Line( p1, p2 );


pShape->draw();
pShape->calcArea();
}
Example – V Table
Shape vTable
… calcArea
0 draw Line object

Line vTable Shape …


calcArea point1 = p1
… draw point2 = p2

pShape
Dynamic Dispatch
• For non-virtual functions, compiler just
generates code to call the function

• In case of virtual functions, compiler


generates code to
– access the object
– access the associated vTable
– call the appropriate function
Conclusion
• Polymorphism adds
– Memory overhead due to vTables
– Processing overhead due to extra pointer
manipulation
• However, this overhead is acceptable for
many of the applications
• Moral: “Think about performance
requirements before making a function
virtual”
Object-Oriented Programming
(OOP)
Syed Saqlain Hassan, PhD
Polymorphism – Case Study

A Simple Payroll Application


Problem Statement
• Develop a simple payroll application.
There are three kinds of employees in the
system: salaried employee, hourly
employee, and commissioned employee.
The system takes as input an array
containing employee objects, calculates
salary polymorphically, and generates
report.
OO Model
String
Employee
name pStr
taxRate String
getName operator =
calcSalary operator <<

SalariedEmp HourlyEmp CommEmp


salary hours sales
calcSalary hourlyRate commRate
calcSalary calcSalary
Class Employee
class Employee {
private:
String name;
double taxRate;
public:
Employee( String&, double );
String getName();
virtual double calcSalary() = 0;
}
… Class Employee
Employee::Employee( String& n,
double tr ): name(n){
taxRate = tr;
}

String Employee::getName() {
return name;
}
Class SalariedEmp

class SalariedEmp : public Employee


{
private:
double salary;
public:
SalariedEmp(String&,double,double);
virtual double calcSalary();
}
… Class SalariedEmp
SalariedEmp::SalariedEmp(String& n,
double tr, double sal)
: Employee( n, tr ) {
salary = sal;
}

double SalariedEmp::calcSalary() {
double tax = salary * taxRate;
return salary – tax;
}
Class HourlyEmp

class HourlyEmp : public Employee {


private:
int hours;
double hourlyRate;
public:
HourlyEmp(string&,double,int,double);
virtual double calcSalary();
}
… Class HourlyEmp

HourlyEmp ::HourlyEmp( String& n,


double tr, int h, double hr )
: Employee( n, tr ) {
hours = h;
hourlyRate = hr;
}
… Class HourlyEmp
double HourlyEmp::calcSalary()
{
double grossPay, tax;

grossPay = hours * hourlyRate;


tax = grossPay * taxRate;

return grossPay – tax;


}
Class CommEmp
class CommEmp : public Employee
{
private:
double sales;
double commRate;
public:
CommEmp( String&, double, double,
double
);
virtual double calcSalary();
}
… Class CommEmp

CommEmp::CommEmp( String& n,
double tr, double s, double cr )
: Employee( n, tr ) {
sales = s;
commRate = cr;
}
… Class CommEmp

double CommEmp::calcSalary()
{
double grossPay = sales * commRate;
double tax = grossPay * taxRate;

return grossPay – tax;


}
A Sample Payroll
int main() {
Employee* emp[10];
emp[0] = new SalariedEmp( “Aamir”,
0.05, 15000 );
emp[1] = new HourlyEmp( “Faakhir”,
0.06, 160, 50 );
emp[2] = new CommEmp( “Fuaad”,
0.04, 150000, 10 );

generatePayroll( emp, 10 );
return 0;
}
…A Sample Payroll
void generatePayroll(Employee* emp[],
int size) {

cout << “Name\tNet Salary\n\n”;

for (int i = 0; i < size; i++) {


cout << emp[i]->getName() << ‘\t’
<< emp[i]->calcSalary()
<< ‘\n’;
}
}
Sample Output

Name Net Salary

Aamir 14250
Fakhir 7520
Fuaad 14400

Never Treat Arrays
Polymorphically
Shape Hierarchy Revisited

Shape
draw
calcArea

Line Circle Triangle


draw draw draw
calcArea calcArea calcArea
Shape Hierarchy

class Shape {

public:
Shape();
virtual void draw(){
cout << “Shape\n”;
}
virtual int calcArea() { return 0; }
};
… Shape Hierarchy

class Line : public Shape {



public:
Line(Point p1, Point p2);
void draw(){ cout << “Line\n”; }
}
drawShapes()

void drawShapes( Shape _shape[],


int size ) {
for (int i = 0; i < size; i++) {
_shape[i].draw();
}
}
Polymorphism & Arrays

int main() {
Shape _shape[ 10 ];
_shape[ 0 ] = Shape();
_shape[ 1 ] = Shape();

drawShapes( _shape, 10 );
return 0;
}
Sample Output

Shape
Shape
Shape

…Polymorphism & Arrays

int main() {
Point p1(10, 10), p2(20, 20), …
Line _line[ 10 ];
_line[ 0 ] = Line( p1, p2 );
_line[ 1 ] = Line( p3, p4 );

drawShapes( _line, 10 );
return 0;
}
Sample Output

Shape
// Run-time error
Because
0000 0000
0010
0020 0015
0030 0030
Shape Array 0045

Line Array
_shape[ i ].draw();
*(_shape + (i * sizeof(Shape))).draw();
Original drawShapes()

void drawShapes(Shape* _shape[],


int size) {
for (int i = 0; i < size; i++) {
_shape[i]->draw();
}
}
Sample Output

Line
Line
Line

Because
_line1

0000
0004 _line2
0008
0012 …
_line3
Shape* _shape[]

_shape[i]->draw();
(_shape + (i * sizeof(Shape*)))->draw();

You might also like