0% found this document useful (0 votes)
7 views

W11-Inheritance and Polymorphism in C++(1)

The document provides an overview of inheritance and polymorphism in C++, covering key concepts such as class hierarchies, constructors and destructors in base and derived classes, and the differences between multilevel and multiple inheritance. It explains the importance of access specifiers, the redefinition of base class functions, and the role of virtual functions in achieving runtime polymorphism. The document includes code examples to illustrate these concepts effectively.

Uploaded by

songjiany
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views

W11-Inheritance and Polymorphism in C++(1)

The document provides an overview of inheritance and polymorphism in C++, covering key concepts such as class hierarchies, constructors and destructors in base and derived classes, and the differences between multilevel and multiple inheritance. It explains the importance of access specifiers, the redefinition of base class functions, and the role of virtual functions in achieving runtime polymorphism. The document includes code examples to illustrate these concepts effectively.

Uploaded by

songjiany
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 45

SC1008 C and C++ Programming

Assistant Professor WANG Yong


[email protected]
CCDS, Nanyang Technological University
Week 11
Inheritance and Polymorphism in C++
Outline
• Inheritance
• Constructors and Destructors in Base and Derived
Classes
• Multi-level Inheritance vs. Multiple Inheritance
• Redefine Base Class Functions
• Polymorphism and Virtual Member Functions

3
Inheritance
• Example: Insect Taxonomy

4
Inheritance
• Inheritance is also called an is-a hierarchy
o A poodle is a dog
o A car is a vehicle
o A flower is a plant
o A football player is an athlete
• An object of child class (derived class) is an object of parent
class (base class)
o an UnderGrad is a Student
o a Mammal is an Animal

5
Inheritance
Benefits:
• Code reuse: a derived class can automatically inherit code from
base class
• Polymorphism: Ability to redefine existing behavior but preserve
the interface; children can override the behavior of the parent
• Extensibility: Children can add behavior
class Student // base class
{
. . .
};

class UnderGrad : public Student


{ // derived class
. . .
}; 6
What Does a Child Have?
• An object of the derived class has:
o All members defined in child class
o All members declared in parent class

• An object of the derived class can use:


o All public members defined in child class
o All public members defined in parent class

Note: The following terms are often used interchangeably:


“child class” == “derived class”
“parent class” == “base class”

7
Class Access Specifiers
• public – object of derived class can be treated as object of
base class (not vice-versa)
• protected – more restrictive than public, but allows
derived classes to know details of parents
• private – prevents objects of derived class from being
treated as objects of base class.

8
Inheritance vs. Access

How inherited base class members


Base class members appear in derived class
private: x private inheritance x is inaccessible
protected: y private: y
public: z private: z

private: x protected inheritance x is inaccessible


protected: y protected: y
public: z protected: z

private: x public inheritance x is inaccessible


protected: y protected: y
public: z public: z

Almost always you will want public inheritance 9


Public Inheritance vs. Access

class Grade class Test : public Grade


private members: private members:
char letter; int numQuestions;
float score; float pointsEach;
void calcGrade(); int numMissed;
public members: public members:
void setScore(float); Test(int, int);
float getScore();
char getLetter();

private members:
When Test class inherits int numQuestions:
from Grade class using float pointsEach;
public class access, it looks like this: int numMissed;
public members:
Test(int, int);
void setScore(float);
float getScore();
char getLetter();
10
Protected Inheritance vs. Access

class Grade class Test : protected Grade


private members: private members:
char letter; int numQuestions;
float score; float pointsEach;
void calcGrade(); int numMissed;
public members: public members:
void setScore(float); Test(int, int);
float getScore();
char getLetter();
private members:
int numQuestions:
float pointsEach;
int numMissed;
When Test class inherits from Grade public members:
class using protected class access, it Test(int, int);
protected members:
looks like this: void setScore(float);
float getScore();
float getLetter();

11
Private Inheritance vs. Access

class Grade class Test : private Grade


private members: private members:
char letter; int numQuestions;
float score; float pointsEach;
void calcGrade(); int numMissed;
public members: public members:
void setScore(float); Test(int, int);
float getScore();
char getLetter();
private members:
int numQuestions:
float pointsEach;
int numMissed;
When Test class inherits from Grade void setScore(float);
class using private class access, it float getScore();
looks like this: float getLetter();
public members:
Test(int, int);

12
Constructors and Destructors in Inheritance
• A derived class will NOT inherit the constructors, destructor
or assignment operator from a base class
• Derived classes can have their own constructors and
destructors
• When an object of a derived class is created, the base class’s
constructor is executed first, followed by the derived class’s
constructor
• When an object of a derived class is destroyed, its
destructor is called first, then that of the base class

• Derived class constructors and assignment operators,


however, can call base class constructors and assignment
operators

13
Constructors and Destructors in Inheritance
• The execution order of constructors and destructors
#include <iostream>
using namespace std;

class BaseClass {
public:
BaseClass() { cout << "BaseClass Constructor\n"; }
~BaseClass() { cout << "BaseClass Destructor\n"; }
};

class DerivedClass : public BaseClass {


public:
DerivedClass() { cout << "DerivedClass Constructor\n"; }
~DerivedClass() { cout << "DerivedClass Destructor\n"; }
}; Program output:
int main() { Creating object...
cout << "Creating object...\n"; BaseClass Constructor
DerivedClass object; DerivedClass Constructor
cout << "Exiting program...\n"; Exiting program...
return 0; DerivedClass Destructor
} BaseClass Destructor

14
Constructors and Destructors in Inheritance
Passing arguments to base class constructor
• Allow selection between multiple base class constructors

• Specify arguments to base constructor on derived


constructor heading:

derived class base class (parameterized)


constructor constructor

Derived::Derived(int v1, int v2) : Base(v1), extraValue(v2) {

……
derived constructor base constructor
}
parameters parameter

• Must be done if base class has no default constructor


15
Constructors and Destructors in Inheritance
Passing arguments to base class constructor
#include <iostream>
using namespace std;
class Base {
protected:
int value;
public:
Base(int v); // Constructor declaration
};

Base::Base(int v) : value(v) {
Program output: cout<< "value: " << value << endl;
cout << "Base Constructor\n";
value: 10 }
Base Constructor
value: 10 class Derived : public Base {
extraValue: 20 private:
Derived Constructor int extraValue;
public:
Derived(int v1, int v2);
};

// Derived constructor definition


Derived::Derived(int v1, int v2) : Base(v1), extraValue(v2) {
cout<< "value: " << value << endl;
cout<< "extraValue: " << extraValue << endl;
cout << "Derived Constructor\n";
}

int main() {
Derived d1(10, 20);
return 0;
} 16
Constructors and Destructors in Inheritance
• If the base class has no default #include <iostream>
using namespace std;
constructor, then the constructor of class Base {
derived class must pass arguments protected:
int value;
to the parameterized constructor of public:
the base class Base(int v); // Constructor declaration
};
• Otherwise, the compiler does not
know how to initialize the base Base::Base(int v) : value(v) {
cout << "Base Constructor\n";
class, as base class constructor }
must be executed first class Derived : public Base {
private:
int extraValue;
public:
Derived(int v1, int v2);
};

Error!!! // Derived constructor definition


Derived::Derived(int v1, int v2) {
value = v1;
extraValue = v2;
cout << "Derived Constructor\n";
}

int main() {
Derived d1(10, 20);
return 0;
}
17
Multilevel Inheritance
• A base class can also be derived from another class

18
Multilevel Inheritance
#include <iostream>
using namespace std;

class Vehicle {
public:
Vehicle() { cout << "Vehicle Constructor Called\n"; }
void showVehicle() {
cout << "This is a Vehicle\n";
}
};

class FourWheeler : public Vehicle {


public:
FourWheeler() { cout << "FourWheeler Constructor Called\n"; }
void showFourWheeler() {
cout << "This is a Four-Wheeler\n";
}
};

class Car : public FourWheeler {


public:
Car() { cout << "Car Constructor Called\n"; } Program output:
};
Vehicle Constructor Called
int main() { FourWheeler Constructor Called
Car myCar; Car Constructor Called
myCar.showVehicle(); // From Vehicle class This is a Vehicle
myCar.showFourWheeler(); // From FourWheeler class This is a Four-Wheeler
return 0;
} 19
Multiple Inheritance
• Multiple Inheritance is a feature of C++ where a class can inherit
from more than one base classes

• General format of multiple inheritance in C++

Base classes will be separated by a comma (",") and access mode


for every base class must be specified 20
Multiple Inheritance
• The constructors of inherited classes are called in the same
order in which they are inherited
• The destructors are called in reverse order of constructors
#include <iostream>
using namespace std;

class Vehicle {
public:
Vehicle() { cout << "Vehicle Constructor Called\n"; }
~Vehicle() { cout << "Vehicle Destructor Called\n"; }
};

class FourWheeler {
public:
FourWheeler() { cout << "FourWheeler Constructor Called\n"; }
~FourWheeler() { cout << "FourWheeler Destructor Called\n"; }
};

class Car : public Vehicle, public FourWheeler { Program output:


public:
Car() { cout << "Car Constructor Called\n"; } Vehicle Constructor Called
~Car() { cout << "Car Destructor Called\n"; } FourWheeler Constructor Called
}; Car Constructor Called
Car Destructor Called
int main() { FourWheeler Destructor Called
Car myCar; Vehicle Destructor Called
return 0;
}
21
Redefining Base Class Functions
• A base class member function may be redefined in a
derived class
- The function in a derived class has the same name and
same parameter list as the function in the base class
- Often used to replace a function in the base class with
different actions in a derived class
- It leads to function hiding: Objects of base class use base
class version of function; objects of derived class use derived
class version of function

22
Redefining Base Class Functions
#include <iostream>
using namespace std;

class Base {
public:
void display(int a = 10) {
cout<< "Base class display(): " << a << endl;
}
};

class Derived : public Base {


public:
void display(int a = 100) {//It hides Base::display()
cout << "Derived class display(): " << a << endl;
}
};

int main() {
Base b;
Derived d; Program output:
b.display();
d.display(); Base class display(): 10
return 0; Derived class display(): 100
}

23
Redefining Base Class Functions
– Possible Problem
• Problem: Static Binding -- function calls are bound at the
compile time
#include <iostream>
using namespace std;
class Base {
public:
void display() {
cout<< "Base class display() " << endl;
}
};
class Derived : public Base {
public:
void display() { //It hides Base::display()
cout << "Derived class display() " << endl;
}
}; It will call the
display() function
int main() { in the base class!
Derived d;
d.display();
Program output:
Base* ptr = &d; Derived class display()
ptr->display(); Base class display()
return 0;
}
24
Redefining Base Class Functions
• Redefining vs. overloading
- Overloaded functions have the same function name, but
with a different parameter list
- Overloading can take place inside the same class, i.e.,
multiple member functions of the same class have the same
name

- Overloading can also be enabled between base class and


derived class, but should use
using Base::functionName;
to retain access to base function

25
Overloading a Base Class Function
#include<iostream>
using namespace std;

class Base {
public:
void show() {
cout << "Base class show()" << endl;
}
};

class Derived : public Base {


public:
using Base::show; // This must be kept! Otherwise, Error
void show(int x) { // Overloaded
cout << "Derived class show() with int: " << x << endl;
}
}; Program output:
int main() { Base class show()
Derived class show() with int: 10
Derived d;
d.show(); // Calls Base class show()
d.show(10); // Calls Derived class show(int)
}
26
Polymorphism
• A real-life example of polymorphism -- a person can have different
characteristics:
- A man can have different roles (father, husband, employee) at the
same time
- The same person can behave differently at different situations
• Polymorphism in C++ programming: Polymorphism allows an
object reference variable or an object pointer to 1) reference
different types of objects and 2) call the correct member
functions, depending on the type of object being referenced.

27
Polymorphism
• Example Recall: Can we use base class pointer to call the function
display() in the derived class?
#include <iostream>
using namespace std;
class Base {
public:
void display() {
cout<< "Base class display() " << endl;
}
};
class Derived : public Base {
public:
void display() { //It hides Base::display()
cout << "Derived class display() " << endl;
}
}; Base Class Pointers
int main() {
Derived d;
- Base class pointers and references only know
d.display(); members of the base classes, so you cannot use
a base class pointer to call a derived class function
Base* ptr = &d; - Redefined functions in the derived class will be
ptr->display();
return 0; ignored unless base class declare the function
} as a virtual function 28
Virtual Functions
• Virtual (member) function: a function in base class that expects
to be redefined in derived class and defined with the keyword
virtual:
virtual void Y() {...}

• Supports dynamic binding: The compiler will not bind the


functions at the compile time. Instead, the program will bind
them at the runtime and decide how they will actually behave

• Virtual functions are mainly used to achieve runtime


polymorphism

• Without virtual member functions, C++ uses static binding,


where functions are bound at the compile time

29
Polymorphism
• An example of using virtual function for runtime polymorphism
#include <iostream>
using namespace std;
class Base {
public:
virtual void display() {
cout<< "Base class display()" << endl;
}
};
class Derived : public Base {
public:
void display() {
cout << “Derived class display()” << endl;
}
};
It will call the
int main() {
Base a; display() function
Derived d; in the derived
a.display(); class now!
d.display();

Base* ptr = &a; Program output:


ptr->display();
ptr = &d; Base class display()
ptr->display(); Derived class display()
return 0; Base class display()
} Derived class display() 30
Polymorphism – Important Notes
• When a member function is declared virtual in a base class, any overridden
versions of this function in the derived classes automatically become virtual
• But it is still good to declare the function as virtual in the derived class for
documentation purposes
#include <iostream>
using namespace std;
class Base {
public:
virtual void display() {
cout<< "Base class display()" << endl;
}
};
class Derived : public Base {
public:
virtual void display() {
cout << “Derived class display()” << endl;
}
};

int main() {
Base a;
Derived d;
a.display();
d.display();

Base* ptr = &a;


Program output:
ptr->display(); Base class display()
ptr = &d; Derived class display()
ptr->display();
Base class display()
return 0;
} Derived class display() 31
Polymorphism – Important Notes
• You place the virtual key word only in the function’s declaration or prototype
• If the function is defined outside the class, you do not place the virtual key
word in the function header

#include <iostream>
using namespace std;
class Base {
public:
virtual void display();
};
void Base::display() { cout<< "Base class display()" << endl; }

class Derived : public Base {


public:
void display() { cout << "Derived class display() "<< endl; }
};
int main() {
Base a;
Derived d;
a.display();
d.display();

Base* ptr = &a; Program output:


ptr->display(); Base class display()
ptr = &d;
ptr->display();
Derived class display()
return 0; Base class display()
} Derived class display()

32
Polymorphism – Important Notes
• C++ 11 introduces the keyword override in a derived class to explicitly indicate
the purpose of overriding a virtual function in a base class
• The keyword override enhances the code clarity and enables compiler checking

A Coding Mistake
#include <iostream>
using namespace std;

class Base {
public:
virtual void display(int x) {
cout << "Base display: " << x << endl;
}
};

class Derived : public Base {


public:
void display(double x) {
cout << "Derived display: " << x << endl;
}
};
Program output:

?
int main() {
Derived d; Base display: 42
Base* ptr = &d;
ptr->display(42);
return 0;
}
33
Polymorphism – Important Notes
• C++ 11 introduces the keyword override in a derived class to explicitly indicate
the purpose of overriding a virtual function in a base class
• The keyword override enhances the code clarity and enables compiler checking

A Coding Mistake
#include <iostream>
using namespace std;

class Base {
public:
virtual void display(int x) {
cout << "Base display: " << x << endl;
}
}; With the keyword override, the compiler
can generate error message to remind us
class Derived : public Base { of the code mistake here.
public:
void display(double x) override {
cout << "Derived display: " << x << endl;
}
};

int main() {
Derived d;
Base* ptr = &d;
ptr->display(42);
return 0;
}
34
Virtual Destructor
• You should always declare a destructor virtual, if the class with a
destructor could potentially become a base class

• Reason:
- The compiler will perform static binding on the destructor if
it is not declared virtual
- This can lead to problems when a base class pointer or
reference variable references a derived class object

35
Virtual Destructor
#include <iostream>
using namespace std;

class Animal {
public:
Animal() { cout << "Animal constructor executing.\n"; }
~Animal() { cout << "Animal destructor executing.\n"; }
};

class Dog : public Animal {


public:
Dog() : Animal() {
cout << "Dog constructor executing.\n";
} Only the base
~Dog() { class destructor is
cout << "Dog destructor executing.\n"; executed!
}
};

int main() {
Animal* myAnimal = new Dog(); Program output:
delete myAnimal; Animal constructor executing.
myAnimal = nullptr; Dog constructor executing.
return 0; Animal destructor executing.
}
36
Virtual Destructor
#include <iostream>
using namespace std;

class Animal {
public:
Animal() { cout << "Animal constructor executing.\n"; }
virtual ~Animal() { cout << "Animal destructor executing.\n"; }
};

class Dog : public Animal {


public:
Dog() : Animal() {
cout << "Dog constructor executing.\n"; The destructors of
} both base and
~Dog() {
derived class are
cout << "Dog destructor executing.\n";
} executed!
};

int main() { Program output:


Animal* myAnimal = new Dog();
delete myAnimal; Animal constructor executing.
myAnimal = nullptr; Dog constructor executing.
return 0; Dog destructor executing.
} Animal destructor executing.

37
Overriding vs. Redefining
• Overriding: a derived class overrides a virtual function of the
base class
-- Overridden functions are dynamically bound

• Redefining: a derived class redefines a non-virtual function of


the base class
-- Redefined non-virtual functions are statically bound

• A virtual function is overridden, and a non-virtual function is


redefined

38
Taxonomy of Polymorphism in C++

• Compile-time (Static) Polymorphism:


- Function overloading
- Operator overloading
- Templates of functions or classes (a.k.a., generic programming)
• Run-time (Dynamic) Polymorphism:
- Function overriding

https://www.geeksforgeeks.org/cpp-polymorphism/ 39
Abstract Base Classes and Pure Virtual
Functions
• Pure virtual function is a virtual member function declared in a
base class like this:
virtual void Y() = 0;

• Pure virtual function must have no function definition in the


base class

• Pure virtual function must be overridden in a derived class that


has objects
Abstract Base Classes and Pure Virtual
Functions
• Abstract base class: a class that can have no objects, and
serves as a basis for derived classes that may/will have
objects

• A class becomes an abstract base class when at least one


of its member functions is a pure virtual function
Abstract Base Classes and Pure Virtual
Functions
#include <iostream>
#include <cmath>
using namespace std;
class Shape {
public:
// Pure virtual function
virtual double area() const = 0;
// Virtual destructor (best practice in abstract classes)
virtual ~Shape() {}
};
class Circle : public Shape {
private:
double radius;
public: Program output:
Circle(double r) : radius(r) {} Circle area: 3.14159
// Override the pure virtual function
double area() const override {
return M_PI * radius * radius;
}
};
int main() {
// Shape s; // ERROR: Cannot instantiate Shape directly
Shape* shapePtr = new Circle(1.0);
cout << "Circle area: " << shapePtr->area() << endl;
delete shapePtr;
shapePtr = nullptr;
return 0;
}
Abstract Base Classes and Pure Virtual
Functions
#include <iostream>
#include <cmath>
using namespace std;
class Shape {
public:
// Pure virtual function
virtual double area() const = 0;
// Virtual destructor (best practice in abstract classes)
virtual ~Shape() {}
};
class Circle : public Shape {
private:
double radius;
public: Program output:
Circle(double r) : radius(r) {} Circle area: 3.14159
// Override the pure virtual function
double area() const override {
return M_PI * radius * radius; You can add the keyword “override”
} explicitly to indicate that you
};
intend to do function overriding!
int main() {
// Shape s; // ERROR: Cannot instantiate Shape directly
Shape* shapePtr = new Circle(1.0);
cout << "Circle area: " << shapePtr->area() << endl;
delete shapePtr;
shapePtr = nullptr;
return 0;
}
References:
[1] Tony Gaddis. Starting out with C++ from control structures through objects,
8th edition. Chapter 15.
[2] Prata, Stephen. C++ primer plus. Sams Publishing, 2002, 5th edition.
Chapters 13.
44
Questions?

Thank You!

You might also like