C++ Inheritance and Object-Oriented Concepts
C++ Inheritance and Object-Oriented Concepts
(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
▪ 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
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
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
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
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
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
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
Child
...
Func1
Overriding
class Parent {
public:
void Func1();
void Func1(int);
};
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
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
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 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 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
• 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 )
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 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 GrandParent{
};
Shape
draw
calcArea
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
• Hard to maintain
Solution?
class Shape {
…
virtual void draw();
}
Shape Hierarchy
Shape
draw
calcArea
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
Shape
draw
calcArea
• Cannot be instantiated
• Can be instantiated
class Shape {
…
public:
virtual void draw() = 0;
}
…
Shape s; // Error!
… Pure Virtual Functions
Rectangle
draw
… Pure Virtual Functions
• 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
• Output
Rectangle destructor called
Quadilateral destructor called
Shape destructor called
Result
pShape pShape
Shape Part
Quad Part
Rect Part
Before After
Virtual Functions – Usage
int main() {
Point p1( 10, 10 ), p2( 30, 30 );
Shape* pShape;
pShape
Dynamic Dispatch
• For non-virtual functions, compiler just
generates code to call the function
String Employee::getName() {
return name;
}
Class SalariedEmp
double SalariedEmp::calcSalary() {
double tax = salary * taxRate;
return salary – tax;
}
Class HourlyEmp
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;
Aamir 14250
Fakhir 7520
Fuaad 14400
…
Never Treat Arrays
Polymorphically
Shape Hierarchy Revisited
Shape
draw
calcArea
class Shape {
…
public:
Shape();
virtual void draw(){
cout << “Shape\n”;
}
virtual int calcArea() { return 0; }
};
… Shape Hierarchy
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()
Line
Line
Line
…
Because
_line1
0000
0004 _line2
0008
0012 …
_line3
Shape* _shape[]
_shape[i]->draw();
(_shape + (i * sizeof(Shape*)))->draw();