You are on page 1of 12

4/5/2023

12.1 Introduction 12.1 Introduction


• Inheritance • Polymorphism
– New classes created from existing classes – Write programs in a general fashion
– Absorb attributes and behaviors – Handle a wide variety of existing (and unspecified) related
– Derived class classes
• Class that inherits data members and member functions from a
previously defined base class
– Single inheritance
• Class inherits from one base class
– Multiple inheritance
• Class inherits from multiple base classes
– Types of inheritance
• public: Derived objects are accessible by the base class objects
• private: Derived objects are inaccessible by the base class
• protected: Derived classes and friends can access protected
members of the base class

12.2 Inheritance: Base and Derived Classes 12.2 Inheritance: Base and Derived Classes
• Base and derived classes • Implementation of public inheritance
– Often an object from a derived class (subclass) is also an class CommissionWorker : public Employee {
object of a base class (superclass) ...
• A rectangle is a derived class in reference to a quadrilateral and };
a base class in reference to a square – Class CommissionWorker inherits from class
Employee
• Inheritance examples
Base class Derived classes – friend functions not inherited
Student GraduateStudent – private members of base class not accessible from
UndergraduateStudent
derived class
Shape Circle
Triangle
Rectangle
Loan CarLoan
HomeImprovementLoan
MortgageLoan
Employee FacultyMember
StaffMember
Account CheckingAccount
SavingsAccount

12.4 Casting Base-Class Pointers to Derived


12.3 protected Members
Class Pointers
• protected access • Derived classes relationships to base classes
– Intermediate level of protection between public and – Objects of a derived class can be treated as objects of the base
private inheritance class
– Derived-class members can refer to public and • Reverse not true — base class objects cannot be derived-class objects
protected members of the base class simply by using the
• Downcasting a pointer
member names
– Use an explicit cast to convert a base-class pointer to a derived-
– Note that protected data “breaks” encapsulation
class pointer
– If pointer is going to be dereferenced, the type of the pointer must
match the type of object to which the pointer points
– Format:
derivedPtr = static_cast< DerivedClass * > basePtr;

1
4/5/2023

1 // test. 12.4: point.h


2 // Definition of class Point
12.4 Casting Base-Class Pointers to Derived-
Class Pointers 5
6 #include <iostream>
• The following example: 7
8 using std::ostream;
– Demonstrates the casting of base class pointers to derived 9
10 class Point {
class pointers 11 friend ostream &operator<<( ostream &, const Point & );
12 public:
– Class Circle is derived from class Point 12 Point( int = 0, int = 0 ); // default constructor
14 void setPoint( int, int ); // set coordinates
– A pointer of type Point is used to reference a Circle 15 int getX() const { return x; } // get x coordinate
object, and a pointer to type Circle is used to reference a 16 int getY() const { return y; } // get y coordinate
17 protected: // accessible by derived classes
Point object 18 int x, y; // x and y coordinates of the Point
19 };
20

22 // test. 12.4: point.cpp


23 // Member functions for class Point
24 #include <iostream>
25 #include "point.h"
26
27 // Constructor for class Point
28 Point::Point( int a, int b ) { setPoint( a, b ); }
29
30 // Set x and y coordinates of Point
31 void Point::setPoint( int a, int b )
32 {
33 x = a;

34 y = b; 65 Circle( double r = 0.0, int x = 0, int y = 0 );


35 } 66
36
67 void setRadius( double ); // set radius
37 // Output Point (with overloaded stream insertion operator)
38 ostream &operator<<( ostream &output, const Point &p ) 68 double getRadius() const; // return radius
39 { 69 double area() const; // calculate area
40 output << '[' << p.x << ", " << p.y << ']';
70 protected:
41
42 return output; // enables cascaded calls 71 double radius;
43 } 72 };
44 // test. 12.4: circle.h 73
45 // Definition of class Circle

75 // test. 12.4: circle.cpp


48 76 // Member function definitions for class Circle
49 #include <iostream>
Circle inherits from Point,
77 #include "circle.h"
50 and has Point's data members
78 (which are set by calling
51 using std::ostream;
52 Class Circle publicly inherits from class 79 // Constructor for Circle calls constructor for Point Point's constructor).
53 #include <iomanip> Point, so it will have class Point's public 80 // with a member initializer then initializes radius.
54 and protected member functions and data. 81 Circle::Circle( double r, int a, int b )
55 using std::ios;
56 using std::setiosflags; 82 : Point( a, b ) // call base-class constructor
57 using std::setprecision; 83 { setRadius( r ); }
58
84
59 #include "point.h"
60 85 // Set radius of Circle
61 class Circle : public Point { // Circle inherits from Point 86 void Circle::setRadius( double r )
62 friend ostream &operator<<( ostream &, const Circle & );
87 { radius = ( r >= 0 ? r : 0 ); }
63 public:
64 // default constructor 88

122 Circle *circlePtr = 0, c( 2.7, 120, 89 );


89 // Get radius of Circle Point p: [30, 50]
123
90 double Circle::getRadius() const { return radius; } Circle c: Center = [120, 89]; Radius = 2.70
91 124 cout << "Point p: " << p << "\nCircle c: " << c << '\n';
92 // Calculate area of Circle 125
93 double Circle::area() const
94 { return 3.14159 * radius * radius; } 126 // Treat a Circle as a Point (see only the base class part) Assign derived-class
95 127 pointPtr = &c; // assign address of Circle to pointPtr pointer (&c) to base class
96 // Output a Circle in the form: pointer pointPtr.
128 cout << "\nCircle c (via *pointPtr): "
97 // Center = [x, y]; Radius = #.##
129 << *pointPtr << '\n';
98 ostream &operator<<( ostream &output, const Circle &c )
Circle c (via *pointPtr): [120, 89] The base class pointer only
99 { 120 "sees" the base-class part
100 output << "Center = " << static_cast< Point >( c )
101 << "; Radius = "
121 // Treat a Circle as a Circle (with some casting) of the object it points to.
102 << setiosflags( ios::fixed | ios::showpoint ) 122 // cast base-class pointer to derived-class pointer
103 << setprecision( 2 ) << c.radius; Cast pointPtr into a
123 circlePtr = static_cast< Circle * >( pointPtr );
104 Circle * , and assign to
124 cout << "\nCircle c (via *circlePtr):\n" << *circlePtr
105 return output; // enables cascaded calls circlePtr.
106 } 125 << "\nArea of c (via circlePtr): "
107 // test. 12.4: test09_04.cpp 126 Circle
<< circlePtr->area() << '\n';c (via *circlePtr):
circlePtr will treat c
108 // Casting base-class pointers to derived-class pointers
127
(the object
Center = [120, 89]; Radius Assign pointPtr to atoPoint
which it
109 #include <iostream> = 2.70 points) as a Circle.
110 128 // DANGEROUS: Treat a Point as a Circle object. It has no derived-class
Area of c (via circlePtr):
111 using std::cout; 129 pointPtr = &p; // assign 22.90 information.
address of Point to pointPtr
112 using std::endl; 140 When it is cast to a Circle *,
112
141 // cast base-class pointer to derived-class pointer circlePtr is really assigned to a
114 #include <iomanip>
115 142 circlePtr = static_cast< Circle * >( pointPtr ); base-class object with no derived-
116 #include "point.h" 143 cout << "\nPoint p (via *circlePtr):\n" << *circlePtr class information. This is dangerous.
117 #include "circle.h" 144 << "\nArea of object circlePtr points to: "
118 Point p (via *circlePtr):
145 << circlePtr->area() << endl;
119 int main()
146 return 0; Center = [30, 50]; Radius = 0.00
120 {
121 Point *pointPtr = 0, p( 30, 50 ); 147 } Area of object circlePtr points to: 0.00

2
4/5/2023

Point p: [30, 50]


Circle c: Center = [120, 89]; Radius = 2.70 12.5 Using Member Functions
Circle c (via *pointPtr): [120, 89]

Circle c (via *circlePtr): • Derived class member functions


Center = [120, 89]; Radius = 2.70
Area of c (via circlePtr): 22.90 – Cannot directly access private members of their base
Point p (via *circlePtr): class
Center = [30, 50]; Radius = 0.00
Area of object circlePtr points to: 0.00 • Maintains encapsulation
– Hiding private members is a huge help in testing,
debugging and correctly modifying systems

1 // test. 12.5: employ.h


2 // Definition of class Employee
12.6 Overriding Base-Class Members in a
Derived Class 5
6 class Employee {
• To override a base-class member function 7
8
public:
Employee( const char *, const char * ); // constructor
– In the derived class, supply a new version of that function 9
10
void print() const;
~Employee();
// output first and last name
// destructor
with the same signature 11 private:
12 char *firstName; // dynamically allocated string
• same function name, different definition 12 char *lastName; // dynamically allocated string
14 };
– When the function is then mentioned by name in the derived 15

class, the derived version is automatically called 17 // test. 12.5: employ.cpp


18 // Member function definitions for class Employee
– The scope-resolution operator may be used to access the 19 #include <iostream>
base class version from the derived class 20
21 using std::cout;
22
23 #include <cstring>
24 #include <cassert>
25 #include "employ.h"
26
27 // Constructor dynamically allocates space for the
28 // first and last name and uses strcpy to copy
29 // the first and last names into the object.
30 Employee::Employee( const char *first, const char *last )
31 {
32 firstName = new char[ strlen( first ) + 1 ];

33 assert( firstName != 0 ); // terminate if not allocated 64 double wage; // wage per hour
34 strcpy( firstName, first ); 65 double hours; // hours worked for week
35 66 };
36 lastName = new char[ strlen( last ) + 1 ]; 67
37
38
assert( lastName != 0 ); // terminate if not allocated
strcpy( lastName, last ); • 1.1 Function 69 // test. 12.5: hourly.cpp
39 } 70 // Member function definitions for class HourlyWorker
40 definitions 71 #include <iostream>
41 // Output employee name 72
42 void Employee::print() const 73 using std::cout;
43 { cout << firstName << ' ' << lastName; } 74 using std::endl;
44
45 // Destructor deallocates dynamically allocated memory • ---------------- 75
76 #include <iomanip>
46 Employee::~Employee()
47 { ----- 77
78 using std::ios;
48 delete [] firstName; // reclaim dynamic memory 79 using std::setiosflags;
49 delete [] lastName; // reclaim dynamic memory 80 using std::setprecision;
50 } 81
51 // test. 12.5: hourly.h
52 // Definition of class HourlyWorker
• 1.
HourlyWorker inherits
82
83
#include "hourly.h"

HourlyWo
from Employee. 84 // Constructor for class HourlyWorker
85 HourlyWorker::HourlyWorker( const char *first,
rker class 86 const char *last,
56 #include "employ.h" 87 double initHours, double initWage )
57 definition
HourlyWorker will override
88 : Employee( first, last ) // call base-class constructor
89 {
58 class HourlyWorker : public Employee { the print function. 90 hours = initHours; // should validate
59 public:
91 wage = initWage; // should validate
60 HourlyWorker( const char*, const char*, double, double );
92 }
61 double getPay() const; // calculate and return salary 93
62 void print() const; // overridden base-class print 94 // Get the HourlyWorker's pay
63 private: 95 double HourlyWorker::getPay() const { return wage * hours; }

3
4/5/2023

96
97 // Print the HourlyWorker's name and pay
98 void HourlyWorker::print() const 12.7 public, private, and protected Inheritance
99 {
100 cout << "HourlyWorker::print() is executing\n\n";
Base class Type of inheritance
101 Employee::print(); // call base-class print function member
102 access public protected private
specifier inheritance inheritance inheritance
103 cout << " is an hourly worker with pay of $"
104 << setiosflags( ios::fixed | ios::showpoint ) public in derived class. protected in derived class. private in derived class.
105 << setprecision( 2 ) << getPay() << endl;
Can be accessed directly by any Can be accessed directly by all Can be accessed directly by all
The print function is Public non-static member functions, non-static member functions non-static member functions
106 }
107 // test. 12.5: test09_05.cpp overriden in friend functions and non- and friend functions. and friend functions.
member functions.
108 // Overriding a base-class member function in a HourlyWorker.
protected in derived class. protected in derived class. private in derived class.
109 // derived class.
However, the new
Can be accessed directly by all Can be accessed directly by all Can be accessed directly by all
function still can call the
110 #include "hourly.h" Protected non-static member functions non-static member functions non-static member functions
original print function and friend functions. and friend functions. and friend functions.
111
using ::
112 int main() Hidden in derived class. Hidden in derived class. Hidden in derived class.
112 {
Can be accessed by non-static Can be accessed by non-static Can be accessed by non-static
member functions and friend member functions and friend member functions and friend
114 HourlyWorker h( "Bob", "Smith", 40.0, 10.00 ); Private
functions through public or functions through public or functions through public or
115 h.print(); protected member functions protected member functions protected member functions
116 return 0; of the base class. of the base class. of the base class.
117 }
HourlyWorker::print() is executing

Bob Smith is an hourly worker with pay of $400.00

12.9 Using Constructors and Destructors in


12.8 Direct and Indirect Base Classes
Derived Classes
• Direct base class • Base class initializer
– Explicitly listed derived class’s header with the colon (:) – Uses member-initializer syntax
notation when that derived class is declared – Can be provided in the derived class constructor to call the
class HourlyWorker : public Employee base-class constructor explicitly
• Employee is a direct base class of HourlyWorker • Otherwise base class’s default constructor called implicitly
• Indirect base class – Base-class constructors and base-class assignment operators
are not inherited by derived classes
– Not listed in derived class’s header
• Derived-class constructors and assignment operators, however,
– Inherited from two or more levels up the class hierarchy can call base-class constructors and assignment operators
class MinuteWorker : public HourlyWorker
• Employee is an indirect base class of MinuteWorker

1 // test. 12.7: point2.h


2 // Definition of class Point
12.9 Using Constructors and Destructors in
Derived Classes 5 • 1. Point
6 class Point {
• A derived-class constructor 7 public: definition
8 Point( int = 0, int = 0 ); // default constructor
– Calls the constructor for its base class first to initialize its 9 ~Point(); // destructor
base-class members 10 protected: // accessible by derived classes
11 int x, y; // x and y coordinates of Point
– If the derived-class constructor is omitted, its default 12 }; • ----------------
constructor calls the base-class’ default constructor 12
-
• Destructors are called in the reverse order of 15
16
// test. 12.7: point2.cpp
// Member function definitions for class Point • 1. Load
17 #include <iostream>
constructor calls 18 header
19 using std::cout;
– So a derived-class destructor is called before its base-class 20 using std::endl;
destructor 21
22 #include "point2.h"
23
24 // Constructor for class Point
• 1.1 Function
25
26
Point::Point( int a, int b )
{
definitions
27 x = a;
28 y = b;
29
30 cout << "Point constructor: "
31 << '[' << x << ", " << y << ']' << endl;
32 }

4
4/5/2023

33 58 // test. 12.7: circle2.cpp


34 // Destructor for class Point 59 // Member function definitions for class Circle
35 Point::~Point() 60 #include <iostream>
36 { 61
37 cout << "Point destructor: " 62 using std::cout;
38 << '[' << x << ", " << y << ']' << endl; 63 using std::endl;
39 } 64
40 // test. 12.7: circle2.h 65 #include "circle2.h"
41 // Definition of class Circle 66
67 // Constructor for Circle calls constructor for Point
68 Circle::Circle( double r, int a, int b )
44 Circle inherits from 69 : Point( a, b ) // call base-class constructor
45 #include "point2.h" Point. Constructor for Circle
70 {
46 calls constructor for
71 radius = r; // should validate
47 class Circle : public Point { Point, first. Uses
72 cout << "Circle constructor: radius is " member-initializer syntax.
48 public:
73 << radius << " [" << x << ", " << y << ']' << endl;
49 // default constructor
74 }
50 Circle( double r = 0.0, int x = 0, int y = 0 );
75
51
76 // Destructor for class Circle
52 ~Circle();
77 Circle::~Circle() Destructor for Circle
53 private:
78 {
calls destructor for Point,
54 double radius;
last.
55 }; 79 cout << "Circle destructor: radius is "

56 80 << radius << " [" << x << ", " << y << ']' << endl;
81 }

82 // test. 12.7: test09_07.cpp Point constructor: [11, 22]


Point destructor: [11, 22]
83 // Demonstrate when base-class and derived-class
84 // constructors and destructors are called. Point constructor: [72, 29]
Circle constructor: radius is 4.5 [72, 29]
85 #include <iostream>
86 Point constructor: [5, 5]
Circle constructor: radius is 10 [5, 5]
87 using std::cout;
88 using std::endl;
Point
• 1. Load
constructor: [72, 29]
Circle constructor: radius is 4.5 [72, 29] Circle destructor: radius is 10 [5, 5]
89
Object created inside a block destroyed
headers Point
Circle
destructor:
destructor:
[5, 5]
radius is 4.5 [72, 29]
90 #include "point2.h" Point constructor: [5, 5] Point destructor: [72, 29]
once it leaves scope.
91 #include "circle2.h" Circle constructor: radius is 10 [5, 5]
92
93 int main()
• 1.1 Initialize
Remember that the Point
94 { objects
constructor is called for
Circle objects before the
95 // Show constructor andPoint constructor:
destructor [11,
calls for 22]
Point
96 { Point destructor: [11, 22] Circle constructor (inside
to out).
97 Point p( 11, 22 );
98 } • 2. Objects
99
100 cout << endl;
Circle destructor: enter and
radius is 10 [5, 5]
Point destructor: [5, 5]
101 Circle circle1( 4.5, 72, 29 );
Circle destructor:
leave scope
radius is 4.5 [72, 29]
102 cout << endl;
Point destructor: [72, 29]
103 Circle circle2( 10, 5, 5 );
104 cout << endl; Point destructor called after
105 return 0; Circle destructor (outside
106 }
in).

12.10 Implicit Derived-Class Object to Base- 12.10 Implicit Derived-Class Object to Base-
Class Object Conversion Class Object Conversion
• Assignment of derived and base classes • Mixing base and derived class pointers and objects
– Derived-class type and base-class type are different – Referring to a base-class object with a base-class pointer
– Derived-class object can be treated as a base-class object • Allowed
• Derived class has members corresponding to all of the base – Referring to a derived-class object with a derived-class pointer
class’s members • Allowed
• Derived-class has more members than the base-class object – Referring to a derived-class object with a base-class pointer
• Base-class can be assigned a derived-class • Possible syntax error
– Base-class object cannot be treated as a derived-class object • Code can only refer to base-class members, or syntax error
• Would leave additional derived class members undefined – Referring to a base-class object with a derived-class pointer
• Derived-class cannot be assigned a base-class • Syntax error
• Assignment operator can be overloaded to allow such an • The derived-class pointer must first be cast to a base-class pointer
assignment

5
4/5/2023

12.11 Software Engineering With Inheritance 12.12 Composition vs. Inheritance


• Classes are often closely related • “Is a” relationships
– “Factor out” common attributes and behaviors and place – Inheritance
these in a base class • Relationship in which a class is derived from another class
– Use inheritance to form derived classes • “Has a” relationships
• Modifications to a base class – Composition
– Derived classes do not change as long as the public and • Relationship in which a class contains other classes as
protected interfaces are the same members
– Derived classes may need to be recompiled

12.13 “Uses A” And “Knows A” Relationships 12.14 Multiple Inheritance


• “Uses a” • Multiple Inheritance
– One object issues a function call to a member function of – Derived-class inherits from multiple base-classes
another object – Encourages software reuse, but can create ambiguities
• “Knows a”
– One object is aware of another
• Contains a pointer or handle to another object
– Also called an association

1 // test. 12.11: base1.h 29 // test. 12.11: derived.h


2 // Definition of class Base1 30 // Definition of class Derived which inherits
31 // multiple base classes (Base1 and Base2).

6 class Base1 {
7 public: 35 #include <iostream>
8 Base1( int x ) { value = x; } 36
9 int getData() const { return value; } 37 using std::ostream;
10 protected: // accessible to derived classes
Derived inherits from
38
Base1 and Base2.
11 int value; // inherited by derived class 39 #include "base1.h"
12 }; 40 #include "base2.h"
12 41
42 // multiple inheritance
15 // test. 12.11: base2.h 43 class Derived : public Base1, public Base2 {
16 // Definition of class Base2 44 friend ostream &operator<<( ostream &, const Derived & );
45
46 public:

20 class Base2 { 47 Derived( int, char, double );


21 public: 48 double getReal() const;
22 Base2( char c ) { letter = c; } 49
23 char getData() const { return letter; } 50 private:
24 protected: // accessible to derived classes
51 double real; // derived class's private data
25 char letter; // inherited by derived class
52 };
26 };
53

6
4/5/2023

55 // test. 12.11: derived.cpp 86 #include "derived.h"


56 // Member function definitions for class Derived 87
57 #include "derived.h" 88 int main()
58 89 { Object b1 contains integer 10
59 // Constructor for Derived calls constructors for 90 Base1 b1( 10 ), *base1Ptr = 0; // create Base1 object Object b2 contains character Z
60 // class Base1 and class Base2. 91 Base2 b2( 'Z' ), *base2Ptr = 0; // create Base2 object Object d contains:
61 // Use member initializers to call base-class constructors 92 Derived d( 7, 'A', 3.5 ); // create Derived object
Integer: 7
62 Derived::Derived( int i, char c, double f ) 93
63 : Base1( i ), Base2( c ), real ( f ) { } 94 // print data members of base class objects Character: A
64 95 cout << "Object b1 contains integer " << b1.getData() Real number: 3.5
65 // Return the value of real 96 << "\nObject b2 contains character " << b2.getData()
66 double Derived::getReal() const { return real; } 97 << "\nObject d contains:\n" << d << "\n\n";
67 98
Data members of Derived can be accessed
68 // Display all the data members of Derived 99 // print data members of derived class object individually:
69 ostream &operator<<( ostream &output, const Derived &d ) 100 // scope resolution operator resolves getData ambiguity
Integer: 7
70 { 101 cout << "Data members of Derived can be"
71 output << " Integer: " << d.value 102 << " accessed individually:" Character: A
72 << "\n Character: " << d.letter 103 << "\n Integer: " << d.Base1::getData() Real number: 3.5
73 << "\nReal number: " << d.real; 104 << "\n Character: " << d.Base2::getData()
74 105 << "\nReal number: " << d.getReal() << "\n\n";
75 return output; // enables cascaded calls 106 Treat d as a Base1
76 } 107 cout << "Derived can be treated as an " object.
77 // test. 12.11: test09_11.cpp 108 << "object of either base class:\n";
78 // Driver for multiple inheritance example 109 Derived can be treated as an object of either base class:
79 #include <iostream> 110 // treat Derived as a Base1 object
80 111 base1Ptr = &d;
81 using std::cout; 112 cout << "base1Ptr->getData() yields "
82 using std::endl; 112 << base1Ptr->getData() << '\n';
Treat d as a Base2 object.
83 114
84 #include "base1.h" 115 // treat Derived as a Base2 object base1Ptr->getData() yields 7
85 #include "base2.h" 116 base2Ptr = &d;

117 cout << "base2Ptr->getData() yields "


base2Ptr->getData() yields A
118 << base2Ptr->getData() << endl;

119
12.15 The diamond problem
120 return 0;

121 }
• The Diamond Problem occurs when a child class
inherits from two parent classes who both share a
Object b1 contains integer 10 common grandparent class. This is illustrated in
Object b2 contains character Z
Object d contains:
Integer: 7
the diagram below:
Character: A
Real number: 3.5

Data members of Derived can be accessed individually:


Integer: 7
Character: A
Real number: 3.5

Derived can be treated as an object of either base class:


base1Ptr->getData() yields 7
base2Ptr->getData() yields A

12.15 The diamond problem (continued..) 12.15 The diamond problem demo
• Here, we have a class Child inheriting from
classes Father and Mother. These two classes, in
turn, inherit the class Person because both Father
and Mother are Person.
• As shown in the testure, class Child inherits the
traits of class Person twice—once from Father and
again from Mother. This gives rise to ambiguity
since the compiler fails to understand which way
to go.
• This scenario gives rise to a diamond-shaped
inheritance graph and is famously called “The
Diamond Problem.”

7
4/5/2023

12.15 The diamond problem (continued..) 12.16 How to Fix The diamond problem?
• Now you can see the ambiguity here. The Person • The solution to the diamond problem is to use
class constructor is called twice: once when the the virtual keyword. We make the two parent
Father class object is created and next when the classes (who inherit from the same grandparent
Mother class object is created. The properties of class) into virtual classes in order to avoid two
the Person class are inherited twice, giving rise to copies of the grandparent class in the child class.
ambiguity.
• Since the Person class constructor is called twice,
the destructor will also be called twice when the
Child class object is destructed.
• Now if you have understood the problem
correctly, let’s discuss the solution to the Diamond
Problem.

12.17 The diamond problem Solution 12.17 The diamond problem Solution
• Here we have used the virtual keyword when classes
Father and Mother inherit the Person class. This is
usually called “virtual inheritance," which guarantees
that only a single instance of the inherited class (in this
case, the Person class) is passed on.
• In other words, the Child class will have a single
instance of the Person class, shared by both the Father
and Mother classes. By having a single instance of the
Person class, the ambiguity is resolved.
• The output of the above code is given below:

12.17 The diamond problem Solution 12.18 virtual Functions


• Here you can see that the class Person constructor is called only • virtual functions
once. – Suppose a set of shape classes such as Circle, Triangle, etc.
• One thing to note about virtual inheritance is that even if the – Every shape has own unique draw function but possible to call
parameterized constructor of the Person class is explicitly called them by calling the draw function of base class Shape
• Compiler determines dynamically (i.e., at run time) which to call
by Father and Mother class constructors through initialization
– In base-class declare draw to be virtual
lists, only the base constructor of the Person class will be
called. – Override draw in each of the derived classes
– virtual declaration:
• This is because there's only a single instance of a virtual base • Keyword virtual before function prototype in base-class
class that's shared by multiple classes that inherit from it. virtual void draw() const;
• To prevent the base constructor from running multiple times, the – A base-class pointer to a derived class object will call the correct
constructor for a virtual base class is not called by the class draw function
inheriting from it. Instead, the constructor is called by the Shape->draw();
constructor of the concrete class. – If a derived class does not define a virtual function, the
function is inherited from the base class

8
4/5/2023

12.18 Virtual Functions 12.19 Abstract and Concrete Classes


ShapePtr->Draw(); • Abstract classes
– Compiler implements dynamic binding – Sole purpose is to provide a base class for other classes
– Function determined during execution time – No objects of an abstract base class can be instantiated
• Too generic to define real objects (i.e., TwoDimensionalShape)
ShapeObject.Draw(); • Can have pointers and references
– Compiler implements static binding
• Concrete classes
– Function determined during compile-time
– Classes that can instantiate objects
– Provide specifics to make real objects (i.e., Square, Circle)
• Making abstract classes
– Declare one or more virtual functions as “pure” by
initializing the function to zero
– Example of a pure virtual function:
virtual double earnings() const = 0;

12.21 Case Study: A Payroll System Using


12.20 Polymorphism
Polymorphism
• Polymorphism • The following example is a payroll system
– Ability for objects of different classes to respond differently to the – Uses virtual functions and polymorphism to perform
same function call payroll calculations based on the type of an employee
– Implemented through virtual functions
• Base-class pointer (or reference) calls a virtual function
• C++ chooses the correct overridden function in object
– If non-virtual member function defined in multiple classes and
called from base-class pointer then the base-class version is used
• If called from derived-class pointer then derived-class version is used
– Suppose print is not a virtual function
Employee e, *ePtr = &e;
HourlyWorker h, *hPtr = &h;
ePtr->print(); // call base-class print function
hPtr->print(); // call derived-class print
function
ePtr=&h; // allowable implicit conversion
ePtr->print(); // still calls base-class print

1 // test. 12.1: employ2.h 22 // test. 12.1: employ2.cpp


23 // Member function definitions for
2 // Abstract base class Employee
24 // abstract base class Employee.
25 // Note: No definitions given for pure virtual functions.
26 #include <iostream>
27
28 using std::cout;
29
6 class Employee { 30 #include <cstring>
7 public: 31 #include <cassert>
32 #include "employ2.h"
8 Employee( const char *, const char * ); 33
9 ~Employee(); // destructor reclaims memory 34 // Constructor dynamically allocates space for the
35 // first and last name and uses strcpy to copy
10 const char *getFirstName() const; 36 // the first and last names into the object.
11 const char *getLastName() const; earnings is 37 Employee::Employee( const char *first, const char *last )
38 {
declared pure
39 firstName = new char[ strlen( first ) + 1 ];
virtual because
13 // Pure virtual function makes Employee abstract base class 40 assert( firstName != 0 ); // test that new worked
the implementation 41 strcpy( firstName, first );
13 virtual double earnings() const = 0; // pure virtual will depend on which 42
15 virtual void print() const; // virtual
derived class it will 43 lastName = new char[ strlen( last ) + 1 ];
be used in. 44 assert( lastName != 0 ); // test that new worked
16 private: 45 strcpy( lastName, last );
Employee is an 46 }
17 char *firstName;
abstract base class. 47
18 char *lastName; 48 // Destructor deallocates dynamically allocated memory
49 Employee::~Employee()
19 };
50 {
20 51 delete [] firstName;
52 delete [] lastName;
53 }

9
4/5/2023

54 76 // test. 12.1: boss1.h

55 // Return a pointer to the first name


77 // Boss class derived from Employee
56 // Const return type prevents caller from modifying private

57 // data. Caller should copy returned string before destructor

58 // deletes dynamic storage to prevent undefined pointer.

59 const char *Employee::getFirstName() const 80 #include "employ2.h"


60 {

61 return firstName; // caller must delete memory

62 } 82 class Boss : public Employee {

63 83 public:
64 // Return a pointer to the last name
84 Boss( const char *, const char *, double = 0.0 );
65 // Const return type prevents caller from modifying private
85 void setWeeklySalary( double );
66 // data. Caller should copy returned string before destructor

67 // deletes dynamic storage to prevent undefined pointer. 86 virtual double earnings() const;

68 const char *Employee::getLastName() const 87 virtual void print() const;


69 {
88 private:
70 return lastName; // caller must delete memory

71 } 89 double weeklySalary;

72 90 };
73 // Print the name of the Employee
91
74 void Employee::print() const

75 { cout << firstName << ' ' << lastName; }

93 // test. 12.1: boss1.cpp 119 // test. 12.1: commis1.h


94 // Member function definitions for class Boss 120 // CommissionWorker class derived from Employee
95 #include <iostream>
96
97 using std::cout;
123 #include "employ2.h"
98
99 #include "boss1.h"
100 125 class CommissionWorker : public Employee {

101 // Constructor function for class Boss 126 public:


102 Boss::Boss( const char *first, const char *last, double s ) 127 CommissionWorker( const char *, const char *,
103 : Employee( first, last ) // call base-class constructor 128 double = 0.0, double = 0.0,
104 { setWeeklySalary( s ); }
129 int = 0 );
105
130 void setSalary( double );
106 // Set the Boss's salary
131 void setCommission( double );
107 void Boss::setWeeklySalary( double s )
Notice the overriden 132 void setQuantity( int );
108 { weeklySalary = s > 0 ? s : 0; } earnings and
109 133 virtual double earnings() const;
print functions.
110 // Get the Boss's pay 134 virtual void print() const;
They were declared
111 double Boss::earnings() const { return weeklySalary; } 135 private:
virtual in the base
112 136 double salary; // base salary per week
class.
113 // Print the Boss's name
137 double commission; // amount per item sold
113 void Boss::print() const
138 int quantity; // total items sold for week
115 {
139 };
116 cout << "\n Boss: ";
117 Employee::print(); 130

118 }

132 // test. 12.1: commis1.cpp 163

133 // Member function definitions for class CommissionWorker 164 // Set CommissionWorker's commission
134 #include <iostream> 165 void CommissionWorker::setCommission( double c )
135
166 { commission = c > 0 ? c : 0; }
136 using std::cout;
167
137
168 // Set CommissionWorker's quantity sold
138 #include "commis1.h"
169 void CommissionWorker::setQuantity( int q )
139
170 { quantity = q > 0 ? q : 0; }
150 // Constructor for class CommissionWorker
Notice the overriden
151 CommissionWorker::CommissionWorker( const char *first, 171 earnings and
152 const char *last, double s, double c, int q ) 172 // Determine CommissionWorker's earnings print functions.
153 : Employee( first, last ) // call base-class constructor 173 double CommissionWorker::earnings() const They were declared
154 {
virtual in the base
174 { return salary + commission * quantity; }
class.
155 setSalary( s );
175
156 setCommission( c );
176 // Print the CommissionWorker's name
157 setQuantity( q );
177 void CommissionWorker::print() const
158 }
178 {
159
179 cout << "\nCommission worker: ";
160 // Set CommissionWorker's weekly base salary

161 void CommissionWorker::setSalary( double s ) 180 Employee::print();

162 { salary = s > 0 ? s : 0; } 181 }

10
4/5/2023

182 // test. 12.1: piece1.h 202 // test. 12.1: piece1.cpp


203 // Member function definitions for class PieceWorker
183 // PieceWorker class derived from Employee 204 #include <iostream>
205
206 using std::cout;
207
208 #include "piece1.h"
186 #include "employ2.h" 209
210 // Constructor for class PieceWorker
211 PieceWorker::PieceWorker( const char *first, const char *last,
212 double w, int q )
188 class PieceWorker : public Employee {
213 : Employee( first, last ) // call base-class constructor
189 public: 213 {
215 setWage( w );
190 PieceWorker( const char *, const char *, 216 setQuantity( q );
217 }
191 double = 0.0, int = 0); 218
219 // Set the wage
192 void setWage( double );
220 void PieceWorker::setWage( double w )
193 void setQuantity( int ); 221 { wagePerPiece = w > 0 ? w : 0; }
222
194 virtual double earnings() const; 223 // Set the number of items output
224 void PieceWorker::setQuantity( int q ) Again, notice the
195 virtual void print() const; 225 { quantity = q > 0 ? q : 0; } overridden
196 private:
226 earnings and
227 // Determine the PieceWorker's earnings print functions.
197 double wagePerPiece; // wage for each piece output 228 double PieceWorker::earnings() const
229 { return quantity * wagePerPiece; } They were declared
198 int quantity; // output for week 230
virtual in the base
231 // Print the PieceWorker's name
199 }; 232 void PieceWorker::print() const class.
233 {
200
234 cout << "\n Piece worker: ";
235 Employee::print();
236 }

237 // test. 12.1: hourly1.h 257 // test. 12.1: hourly1.cpp

238 // Definition of class HourlyWorker 258 // Member function definitions for class HourlyWorker

259 #include <iostream>

260

241 #include "employ2.h" 261 using std::cout;

262

263 #include "hourly1.h"


243 class HourlyWorker : public Employee {
264
244 public:
265 // Constructor for class HourlyWorker
245 HourlyWorker( const char *, const char *,
266 HourlyWorker::HourlyWorker( const char *first,
246 double = 0.0, double = 0.0);
267 const char *last,
247 void setWage( double );
268 double w, double h )
248 void setHours( double );
269 : Employee( first, last ) // call base-class constructor
249 virtual double earnings() const;
270 {
250 virtual void print() const;
271 setWage( w );
251 private:
272 setHours( h );
252 double wage; // wage per hour
273 }
253 double hours; // hours worked for week 274
254 }; 275 // Set the wage

276 void HourlyWorker::setWage( double w )

277 { wage = w > 0 ? w : 0; }

278 298 // test. 12.1: test10_01.cpp


299 // Driver for Employee hierarchy
279 // Set the hours worked
300 #include <iostream>
280 void HourlyWorker::setHours( double h ) 301
281 { hours = h >= 0 && h < 168 ? h : 0; } 302 using std::cout;
303 using std::endl;
282
304
283 // Get the HourlyWorker's pay 305 #include <iomanip>
306
284 double HourlyWorker::earnings() const
307 using std::ios;
285 { 308 using std::setiosflags;
286 if ( hours <= 40 ) // no overtime Overridden functions. 309 using std::setprecision;
310
287 return wage * hours;
311 #include "employ2.h"
288 else // overtime is paid at wage * 1.5 312 #include "boss1.h"
313 #include "commis1.h"
289 return 40 * wage + ( hours - 40 ) * wage * 1.5;
313 #include "piece1.h"
290 } 315 #include "hourly1.h"
291 316
317 void virtualViaPointer( const Employee * );
292 // Print the HourlyWorker's name
318 void virtualViaReference( const Employee & );
293 void HourlyWorker::print() const 319
320 int main()
294 {
321 {
295 cout << "\n Hourly worker: "; 322 // set output formatting
296 Employee::print(); 323 cout << setiosflags( ios::fixed | ios::showpoint )
324 << setprecision( 2 );
297 }
325

11
4/5/2023

326 Boss b( "John", "Smith", 800.00 ); Boss: John Smith earned $800.00 360
327 b.print(); // static binding
Boss: John Smith earned $800.00 361 // Make virtual function calls off a base-class reference
328 cout << " earned $" << b.earnings(); // static binding
329 virtualViaPointer( &b ); // uses dynamic
Boss: binding
John Smith earned $800.00 362 // using dynamic binding.
330 virtualViaReference( b ); // uses dynamic binding
331 363 void virtualViaReference( const Employee &baseClassRef )
332 Call function
CommissionWorker c( "Sue", "Jones", 200.0, 3.0, 150 ); print using the object itself.
364 { Take in base class reference, call
333 c.print(); // static binding
Commission worker: Sue Jones earned $650.00 the virtual function print.
334 cout << " earned $" << c.earnings(); // static binding 365 baseClassRef.print();
335 virtualViaPointer( &c ); // usesCommission worker: Sue Jones earned $650.00
dynamic binding
336 virtualViaReference( c ); // usesCommission
dynamic binding 366 cout << " earned $" << baseClassRef.earnings();
worker: Sue Jones earned $650.00
337 367 }
338 PieceWorker p( "Bob", "Lewis", 2.5, 200 );Call function print, using a base-class pointer.
339 p.print(); // static binding
340 cout << " earned $" << p.earnings(); This
// uses virtual
static binding functions and dynamic binding. Boss: John Smith earned $800.00
341 virtualViaPointer( &p ); // uses dynamic binding Boss: John Smith earned $800.00
Piece worker: Bob Lewis earned $500.00
342 virtualViaReference( p ); // uses dynamic binding Boss: John Smith earned $800.00
343 Piece worker: Bob Lewis earned $500.00 Commission worker: Sue Jones earned $650.00
344 HourlyWorker h( "Karen", "Price", 12.75, 40 ); Piece worker: Bob Lewis earned $500.00 Commission worker: Sue Jones earned $650.00
345 h.print(); // static binding Commission worker: Sue Jones earned $650.00
346 cout << " earned $" << h.earnings();
Call function print, using a base-
// static binding Piece worker: Bob Lewis earned $500.00
347 virtualViaPointer( &h ); // uses class reference.
dynamic binding Piece worker: Bob Lewis earned $500.00
Piece worker: Bob Lewis earned $500.00
348 virtualViaReference( h ); // uses dynamic binding
349 cout << endl; This uses virtual functions and Hourly worker: Karen Price earned $550.00
Hourly worker: Karen Price earned $550.00
350 return 0; dynamic binding. Hourly worker: Karen Price earned $550.00
351 } Hourly worker: Karen Price earned $550.00
352 Hourly worker: Karen Price earned $550.00
353 // Make virtual function calls off a base-class pointer
354 // using dynamic binding. Hourly worker: Karen Price earned $550.00
355 void virtualViaPointer( const Employee *baseClassPtr )
356 {
357 baseClassPtr->print();
Take in a baseclass pointer, call
358 cout << " earned $" << baseClassPtr->earnings();
359 }
the virtual function print.

12.22 New Classes and Dynamic Binding 12.23 Virtual Destructors


• Polymorphism and virtual functions • Problem:
– Work well when all classes are not known in advance – If a base-class pointer to a derived object is deleted, the
– Use dynamic binding to accommodate new classes being base-class destructor will act on the object
added to a system • Solution:
• Dynamic binding (late binding) – declare a virtual base-class destructor to ensure that the
– Object's type need not be know at compile time for a appropriate destructor will be called
virtual function • virtual function table (vtable)
– virtual function call is matched at run time – Every class with a virtual function has a vtable
– For every virtual function, its vtable has a pointer to the
proper function
• If a derived class has the same function as a base class, then
the function pointer points to the base-class function

12

You might also like