Derived Classes and Inheritance: Chapter 9 D&D
Derived Classes and Inheritance: Chapter 9 D&D
Chapter 9 D&D
Derived Classes
It is sometimes the case that we have a class is nearly what we need. Derived classes acquire the properties of an existing class. The original class is called the base class.
Inheritance
Inheritance
New classes created from existing classes Derived class
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
Loan
Employee Account
Class CommissionWorker inherits from class Employee friend functions not inherited private members of base class not accessible from derived class
protected Members
protected access
Intermediate level of protection between public and private inheritance Derived-class members can refer to public and protected members of the base class simply by using the member names Note that protected data breaks encapsulation
Derived Classes
A derived class inherits member functions of base class. A derived class can be used anywhere the base class is expected.
Derived Classes
A derived class inherits member functions of base class. A derived class can be used anywhere the base class is expected. However, a base class CANNOT be used anywhere the derived class is expected.
An Example
The following example:
Demonstrates the casting of base class pointers to derived class pointers Class Circle is derived from class Point A pointer of type Point is used to reference a Circle object, and a pointer to type Circle is used to reference a Point object
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
// Fig. 9.4: point.h // Definition of class Point #ifndef POINT_H #define POINT_H #include <iostream> using std::ostream; class Point { friend ostream &operator<<( ostream &, const Point & ); public: Point( int = 0, int = 0 ); // default constructor void setPoint( int, int ); // set coordinates int getX() const { return x; } // get x coordinate int getY() const { return y; } // get y coordinate protected: // accessible by derived classes int x, y; // x and y coordinates of the Point }; #endif // Fig. 9.4: point.cpp // Member functions for class Point #include <iostream> #include "point.h" // Constructor for class Point Point::Point( int a, int b ) { setPoint( a, b ); } // Set x and y coordinates of Point void Point::setPoint( int a, int b ) { x = a;
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
y = b; } // Output Point (with overloaded stream insertion operator) ostream &operator<<( ostream &output, const Point &p ) { output << [ << p.x << ", " << p.y << ]; return output; // enables cascaded calls } // Fig. 9.4: circle.h // Definition of class Circle #ifndef CIRCLE_H #define CIRCLE_H #include <iostream> using std::ostream; #include <iomanip> using std::ios; using std::setiosflags; using std::setprecision; #include "point.h" class Circle : public Point { // Circle inherits from Point friend ostream &operator<<( ostream &, const Circle & ); public: // default constructor
65 66 67 68 69
70 protected: 71 72 }; 73 74 #endif 75 // Fig. 9.4: circle.cpp 76 // Member function definitions for class Circle 77 #include "circle.h" 78 79 // Constructor for Circle calls constructor for Point 80 // with a member initializer then initializes radius. 81 Circle::Circle( double r, int a, int b ) 82 : Point( a, b ) // call base-class constructor double radius;
89 // Get radius of Circle 90 double Circle::getRadius() const { return radius; } 91 92 // Calculate area of Circle 93 double Circle::area() const 94 { return 3.14159 * radius * radius; } 95 96 // Output a Circle in the form: 97 // Center = [x, y]; Radius = #.## 98 ostream &operator<<( ostream &output, const Circle &c ) 99 { 100 output << "Center = " << static_cast< Point >( c ) 101 << "; Radius = " 102 << setiosflags( ios::fixed | ios::showpoint ) 103 << setprecision( 2 ) << c.radius; 104 105 return output; // enables cascaded calls 106 } 107 // Fig. 9.4: fig09_04.cpp 108 // Casting base-class pointers to derived-class pointers 109 #include <iostream> 110 111 using std::cout; 112 using std::endl; 113 114 #include <iomanip> 115 116 #include "point.h" 117 #include "circle.h" 118 119 int main() 120 { 121 Point *pointPtr = 0, p( 30, 50 );
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 }
cout << "Point p: " << p << "\nCircle c: " << c << \n;
// Treat a Circle as a Point (see only the base class part) pointPtr = &c; // assign address of Circle to pointPtr
cout << "\nCircle c (via *pointPtr): " << *pointPtr << \n;
// Treat a Circle as a Circle (with some casting) // cast base-class pointer to derived-class pointer circlePtr = static_cast< Circle * >( pointPtr ); cout << "\nCircle c (via *circlePtr):\n" << *circlePtr << "\nArea of c (via circlePtr): " << circlePtr->area() << \n;
// DANGEROUS: Treat a Point as a Circle pointPtr = &p; // assign address of Point to pointPtr
// cast base-class pointer to derived-class pointer circlePtr = static_cast< Circle * >( pointPtr ); cout << "\nPoint p (via *circlePtr):\n" << *circlePtr << "\nArea of object circlePtr points to: " << circlePtr->area() << endl; return 0;
Point p: [30, 50] Circle c: Center = [120, 89]; Radius = 2.70 Circle c (via *pointPtr): [120, 89] Circle c (via *circlePtr): Center = [120, 89]; Radius = 2.70 Area of c (via circlePtr): 22.90 Point p (via *circlePtr): Center = [30, 50]; Radius = 0.00 Area of object circlePtr points to: 0.00
Program Output
Hiding private members is a huge help in testing, debugging and correctly modifying systems
When the function is then mentioned by name in the derived class, the derived version is automatically called The scope-resolution operator may be used to access the base class version from the derived class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// Fig. 9.5: employ.h // Definition of class Employee #ifndef EMPLOY_H #define EMPLOY_H class Employee { public: Employee( const char void print() const; ~Employee(); private: char *firstName; char *lastName; };
*, const char * ); // constructor // output first and last name // destructor // dynamically allocated string // dynamically allocated string
#endif // Fig. 9.5: employ.cpp // Member function definitions for class Employee #include <iostream> using std::cout; #include <cstring> #include <cassert> #include "employ.h" // Constructor dynamically allocates space for the // first and last name and uses strcpy to copy // the first and last names into the object. Employee::Employee( const char *first, const char *last ) { firstName = new char[ strlen( first ) + 1 ];
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
assert( firstName != 0 ); // terminate if not allocated strcpy( firstName, first ); lastName = new char[ strlen( last ) + 1 ]; assert( lastName != 0 ); // terminate if not allocated strcpy( lastName, last ); } // Output employee name void Employee::print() const { cout << firstName << << lastName; } // Destructor deallocates dynamically allocated memory Employee::~Employee() { delete [] firstName; // reclaim dynamic memory delete [] lastName; // reclaim dynamic memory } // Fig. 9.5: hourly.h
52 // Definition of class HourlyWorker 53 #ifndef HOURLY_H 54 #define HOURLY_H 55 56 #include "employ.h" 57 58 class HourlyWorker : public Employee { 59 public: 60 61 62 HourlyWorker( const char*, const char*, double, double ); double getPay() const; void print() const; // calculate and return salary // overridden base-class print
63 private:
10
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
#endif // Fig. 9.5: hourly.cpp // Member function definitions for class HourlyWorker #include <iostream> using std::cout; using std::endl; #include <iomanip> using std::ios; using std::setiosflags; using std::setprecision; #include "hourly.h" // Constructor for class HourlyWorker HourlyWorker::HourlyWorker( const char *first, const char *last, double initHours, double initWage ) : Employee( first, last ) // call base-class constructor { hours = initHours; // should validate wage = initWage; // should validate } // Get the HourlyWorkers pay double HourlyWorker::getPay() const { return wage * hours; }
96 97 // Print the HourlyWorkers name and pay 98 void HourlyWorker::print() const 99 { 100 101 102 103 104 105 cout << " is an hourly worker with pay of $" << setiosflags( ios::fixed | ios::showpoint ) << setprecision( 2 ) << getPay() << endl; cout << "HourlyWorker::print() is executing\n\n"; Employee::print(); // call base-class print function
106 } 107 // Fig. 9.5: fig09_05.cpp 108 // Overriding a base-class member function in a 109 // derived class. 110 #include "hourly.h" 111 112 int main() 113 { 114 115 116 117 } HourlyWorker::print() is executing Bob Smith is an hourly worker with pay of $400.00 HourlyWorker h( "Bob", "Smith", 40.0, 10.00 ); h.print(); return 0;
11
Public
private in derived class. Can be accessed directly only by Can be accessed directly only by all member functions all member functions protected in derived class. private in derived class. Can be accessed directly only by Can be accessed directly only by all member functions all member functions Hidden in derived class. Can be accessed by non-static member functions and friend functions through public or protected member functions of the base class. Hidden in derived class. Can be accessed by non-static member functions and friend functions through public or protected member functions of the base class.
protected in derived class. Can be accessed directly in Protected derived class only by member functions (like private) Hidden in derived class. Can be accessed by non-static member functions and friend Private functions through public or protected member functions of the base class.
12
Base-class constructors and base-class assignment operators are not inherited by derived classes
Derived-class constructors and assignment operators, however, can call base-class constructors and assignment operators
13
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// Fig. 9.7: point2.h // Definition of class Point #ifndef POINT2_H #define POINT2_H class Point { public: Point( int = 0, int = 0 ); // default constructor ~Point(); // destructor protected: // accessible by derived classes int x, y; // x and y coordinates of Point }; #endif // Fig. 9.7: point2.cpp // Member function definitions for class Point #include <iostream> using std::cout; using std::endl; #include "point2.h" // Constructor for class Point Point::Point( int a, int b ) { x = a; y = b; cout << "Point constructor: " << [ << x << ", " << y << ] << endl; }
33 34 // Destructor for class Point 35 Point::~Point() 36 { 37 38 39 } 40 // Fig. 9.7: circle2.h 41 // Definition of class Circle 42 #ifndef CIRCLE2_H 43 #define CIRCLE2_H 44 45 #include "point2.h" 46 47 class Circle : public Point { 48 public: 49 50 51 52 ~Circle(); // default constructor Circle( double r = 0.0, int x = 0, int y = 0 ); cout << "Point destructor: "
14
58 // Fig. 9.7: circle2.cpp 59 // Member function definitions for class Circle 60 #include <iostream> 61 62 using std::cout; 63 using std::endl; 64 65 #include "circle2.h" 66 67 // Constructor for Circle calls constructor for Point 68 Circle::Circle( double r, int a, int b ) 69 70 { 71 72 73 74 } 75 76 // Destructor for class Circle 77 Circle::~Circle() 78 { 79 80 81 } cout << "Circle destructor: radius is " radius = r; // should validate : Point( a, b ) // call base-class constructor
cout << "Circle constructor: radius is " << radius << " [" << x << ", " << y << ] << endl;
Constructor for Circle calls constructor for Point, first. Uses member-initializer syntax.
<< radius << " [" << x << ", " << y << ] << endl;
82 // Fig. 9.7: fig09_07.cpp 83 // Demonstrate when base-class and derived-class 84 // constructors and destructors are called. 85 #include <iostream> 86 87 using std::cout; 88 using std::endl; 89 90 #include "point2.h" 91 #include "circle2.h" 92 93 int main() 94 { 95 96 97 98 99 100 101 102 103 104 105 106 } cout << endl; Circle circle1( 4.5, 72, 29 ); cout << endl; Circle circle2( 10, 5, 5 ); cout << endl; return 0; } // Show constructor and destructor calls for Point { Point p( 11, 22 );
15
Point Point
Point constructor: [72, 29] Circle constructor: radius is 4.5 [72, 29] Point constructor: [5, 5] Circle constructor: radius is 10 [5, 5] Circle Point Circle Point destructor: destructor: destructor: destructor: radius is 10 [5, 5] [5, 5] radius is 4.5 [72, 29] [72, 29]
Program Output
16
Has a relationships
Composition
Relationship in which a class contains other classes as members
17
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// Fig. 9.8: point2.h // Definition of class Point #ifndef POINT2_H #define POINT2_H #include <iostream> using std::ostream; class Point { friend ostream &operator<<( ostream &, const Point & ); public: Point( int = 0, int = 0 ); // default constructor void setPoint( int, int ); // set coordinates int getX() const { return x; } // get x coordinate int getY() const { return y; } // get y coordinate protected: // accessible to derived classes int x, y; // coordinates of the point }; #endif // Fig. 9.8: point2.cpp // Member functions for class Point #include "point2.h" // Constructor for class Point Point::Point( int a, int b ) { setPoint( a, b ); } // Set the x and y coordinates void Point::setPoint( int a, int b ) { x = a;
18
33 34 } 35
y = b;
36 // Output the Point 37 ostream &operator<<( ostream &output, const Point &p ) 38 { 39 40 41 42 } return output; // enables cascading output << [ << p.x << ", " << p.y << ];
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// Fig. 9.9: circle2.h // Definition of class Circle #ifndef CIRCLE2_H #define CIRCLE2_H #include <iostream> using std::ostream; #include "point2.h" class Circle : public Point { friend ostream &operator<<( ostream &, const Circle & ); public: // default constructor Circle( double r = 0.0, int x = 0, int y = 0 ); void setRadius( double ); // set radius double getRadius() const; // return radius double area() const; // calculate area protected: // accessible to derived classes double radius; // radius of the Circle }; #endif // Fig. 9.9: circle2.cpp
26 // Member function definitions for class Circle 27 #include <iomanip> 28 29 using std::ios; 30 using std::setiosflags; 31 using std::setprecision; 32 33 #include "circle2.h"
19
34 35 // Constructor for Circle calls constructor for Point 36 // with a member initializer and initializes radius 37 Circle::Circle( double r, int a, int b ) 38 40 41 // Set radius 42 void Circle::setRadius( double r ) 43 44 45 // Get radius 46 double Circle::getRadius() const { return radius; } 47 48 // Calculate area of Circle 49 double Circle::area() const 50 51 52 // Output a circle in the form: 53 // Center = [x, y]; Radius = #.## 54 ostream &operator<<( ostream &output, const Circle &c ) 55 { 56 57 58 59 60 61 62 } return output; // enables cascaded calls output << "Center = " << static_cast< Point > ( c ) << "; Radius = " << setiosflags( ios::fixed | ios::showpoint ) << setprecision( 2 ) << c.radius; { return 3.14159 * radius * radius; } { radius = ( r >= 0 ? r : 0 ); } : Point( a, b ) // call base-class constructor 39 { setRadius( r ); }
1 2 3 4 5 6 7 8 9
// Fig. 9.10: cylindr2.h // Definition of class Cylinder #ifndef CYLINDR2_H #define CYLINDR2_H #include <iostream> using std::ostream;
10 #include "circle2.h" 11 12 class Cylinder : public Circle { 13 14 15 public: 16 17 18 19 20 21 22 23 24 25 protected: 26 27 }; 28 29 #endif double height; // height of the Cylinder void setHeight( double ); double getHeight() const; double area() const; double volume() const; // set height // return height // calculate and return area // calculate and return volume // default constructor Cylinder( double h = 0.0, double r = 0.0, int x = 0, int y = 0 ); friend ostream &operator<<( ostream &, const Cylinder & );
20
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
// Fig. 9.10: cylindr2.cpp // Member and friend function definitions // for class Cylinder. #include "cylindr2.h" // Cylinder constructor calls Circle constructor Cylinder::Cylinder( double h, double r, int x, int y ) : Circle( r, x, y ) // call base-class constructor { setHeight( h ); } // Set height of Cylinder void Cylinder::setHeight( double h ) { height = ( h >= 0 ? h : 0 ); } // Get height of Cylinder double Cylinder::getHeight() const { return height; } // Calculate area of Cylinder (i.e., surface area) double Cylinder::area() const { return 2 * Circle::area() + 2 * 3.14159 * radius * height; } // Calculate volume of Cylinder double Cylinder::volume() const { return Circle::area() * height; } // Output Cylinder dimensions ostream &operator<<( ostream &output, const Cylinder &c ) {
Circle::area() is overidden.
61 62 63 64 65 } 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
output << static_cast< Circle >( c ) << "; Height = " << c.height; return output; // enables cascaded calls
// Fig. 9.10: fig09_10.cpp // Driver for class Cylinder #include <iostream> using std::cout; using std::endl; #include "point2.h" #include "circle2.h" #include "cylindr2.h" int main() { // create Cylinder object Cylinder cyl( 5.7, 2.5, 12, 23 ); // use get functions to display the Cylinder cout << "X coordinate is " << cyl.getX() << "\nY coordinate is " << cyl.getY() << "\nRadius is " << cyl.getRadius() << "\nHeight is " << cyl.getHeight() << "\n\n"; // use set functions to change the Cylinders attributes cyl.setHeight( 10 ); cyl.setRadius( 4.25 ); cyl.setPoint( 2, 2 ); X coordinate is 12 Y coordinate is 23 Radius is 2.5 Height is 5.7
21
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 }
cout << "The new location, radius, and height of cyl are:\n" << cyl << \n; cout << "The area of cyl is:\n" << cyl.area() << \n; // display the Cylinder as a Point Point &pRef = cyl; // pRef "thinks" it is a Point cout << "\nCylinder printed as a Point is: " << pRef << "\n\n"; // display the Cylinder as a Circle Circle &circleRef = cyl; // circleRef thinks it is a Circle cout << "Cylinder printed as a Circle is:\n" << circleRef << "\nArea: " << circleRef.area() << endl; return 0;
X coordinate is 12 Y coordinate is 23 Radius is 2.5 Height is 5.7 The new location, radius, and height of cyl are: Center = [2, 2]; Radius = 4.25; Height = 10.00 The area of cyl is: 380.53 Cylinder printed as a Point is: [2, 2] Cylinder printed as a Circle is: Center = [2, 2]; Radius = 4.25 Area: 56.74
Multiple Inheritance
Multiple Inheritance
Derived-class inherits from multiple baseclasses Encourages software reuse, but can create ambiguities
22
1 2 3 4 5 6 7 8 9
// Fig. 9.11: base1.h // Definition of class Base1 #ifndef BASE1_H #define BASE1_H
class Base1 { public: Base1( int x ) { value = x; } int getData() const { return value; } // accessible to derived classes // inherited by derived class
// Fig. 9.11: base2.h // Definition of class Base2 #ifndef BASE2_H #define BASE2_H class Base2 { public: Base2( char c ) { letter = c; } char getData() const { return letter; } protected: // accessible to derived classes char letter; // inherited by derived class }; #endif
29 // Fig. 9.11: derived.h 30 // Definition of class Derived which inherits 31 // multiple base classes (Base1 and Base2). 32 #ifndef DERIVED_H 33 #define DERIVED_H 34 35 #include <iostream> 36 37 using std::ostream; 38 39 #include "base1.h" 40 #include "base2.h" 41 42 // multiple inheritance 43 class Derived : public Base1, public Base2 { 44 45 46 public: 47 48 49 50 private: 51 52 }; 53 54 #endif double real; // derived classs private data Derived( int, char, double ); double getReal() const; friend ostream &operator<<( ostream &, const Derived & );
23
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
// Fig. 9.11: derived.cpp // Member function definitions for class Derived #include "derived.h" // Constructor for Derived calls constructors for // class Base1 and class Base2. // Use member initializers to call base-class constructors Derived::Derived( int i, char c, double f ) : Base1( i ), Base2( c ), real ( f ) { } // Return the value of real double Derived::getReal() const { return real; } // Display all the data members of Derived ostream &operator<<( ostream &output, const Derived &d ) { output << " Integer: " << d.value << "\n Character: " << d.letter << "\nReal number: " << d.real; return output; // enables cascaded calls } // Fig. 9.11: fig09_11.cpp // Driver for multiple inheritance example #include <iostream> using std::cout; using std::endl; #include "base1.h" #include "base2.h"
86 #include "derived.h" 87 88 int main() 89 { 90 Base1 b1( 10 ), *base1Ptr = 0; // create Base1 object 91 Base2 b2( Z ), *base2Ptr = 0; // create Base2 object 92 Derived d( 7, A, 3.5 ); // create Derived object 93 94 // print data members of base class objects 95 cout << "Object b1 contains integer " << b1.getData() 96 << "\nObject b2 contains character " << b2.getData() 97 << "\nObject d contains:\n" << d << "\n\n"; 98 99 // print data members of derived class object 100 // scope resolution operator resolves getData ambiguity 101 cout << "Data members of Derived can be" 102 << " accessed individually:" 103 << "\n Integer: " << d.Base1::getData() 104 << "\n Character: " << d.Base2::getData() 105 << "\nReal number: " << d.getReal() << "\n\n"; 106 107 cout << "Derived can be treated as an " 108 << "object of either base class:\n"; 109 110 // treat Derived as a Base1 object 111 base1Ptr = &d; 112 cout << "base1Ptr->getData() yields " 113 << base1Ptr->getData() << \n; 114 115 // treat Derived as a Base2 object 116 base2Ptr = &d;
24
return 0;
Object b1 contains integer 10 Object b2 contains character Z Object d contains: Integer: 7 Character: A Real number: 3.5 Data members Integer: Character: Real number: of Derived can be accessed individually: 7 A 3.5
Derived can be treated as an object of either base class: base1Ptr->getData() yields 7 base2Ptr->getData() yields A
25