260 likes | 389 Views
Chapter 11: Inheritance. Chapter Goals To understand the concepts of inheritance and polymorphism To learn how inheritance is a tool for code reuse To learn how to call base-class constructors and member functions To understand the difference between static and dynamic binding
E N D
Chapter 11: Inheritance Chapter Goals • To understand the concepts of inheritance and polymorphism • To learn how inheritance is a tool for code reuse • To learn how to call base-class constructors and member functions • To understand the difference between static and dynamic binding • To be able to implement dynamic binding with virtual functions
Derived Classes • Inheritance is a mechanism for enhancing existing working classes. • If a new class needs to be implemented and a class representing a more general concept is already available, then the new class can inherit from the existing class. • The existing, more general class is called the base class. • The more specialized class that inherits from the base class is called the derived class. Example: class Manager : public Employee { public: …new member functions private: …new data members }; Class Manager is derived, Employee is base class.
Derived Classes • Derived Class Definition: general syntax class Derived_class_name : public Base_class_name { … new features in derived class}; Example: class Manager : public Employee { public: Manager(string name, double salary, string dept); string get_department() const; private: string department; }; • Purpose: To define a class (the derived class) that inherits features from a base class.
Derived Classes • All member functions and data members of the base class are automatically inherited by the derived class. Example: Manager m; m.set_salary(68000); • In the derived class definition you specify only new member functions and data members. Example: class Manager : public Employee { public: Manager(string name, double salary, string dept); string get_department() const; private: string department; };
Derived Classes • The diagram below shows the relationship between derived classes and corresponding base classes. The arrow with a hollow head indicates inheritance.
Example - Base Class : Clock #ifndef __CLOCK_H_ #define __CLOCK_H_ class Clock {public:/** Constructs a clock that can tell the local time. @param use_military true if the clock uses military format */Clock(bool use_military); // returns the current location string get_location() const; // returns the hours in military format int get_hours() const; // returns the minutes int get_minutes() const;// returns true if military format bool is_military() const;private: bool military; }; #endif
Implementation of class Clock Clock::Clock(bool use_military) { military = use_military;}string Clock::get_location() const {return "Local";}int Clock::get_hours() const { Time now; int hours = now.get_hours();if (military) return hours;if (hours == 0) return 12;elseif (hours > 12)return hours - 12;elsereturn hours;} int Clock::get_minutes() const { Time now;return now.get_minutes(); } bool Clock::is_military() const {return military; }
Example of Program Using Class Clock int main() { Clock clock1(true); Clock clock2(false); bool more = true;while (more) { cout << "Military time: " << clock1.get_hours() << ":" << setw(2) << setfill('0') << clock1.get_minutes() << setfill(' ') << "\n"; cout << "am/pm time: " << clock2.get_hours() << ":" << setw(2) << setfill('0') << clock2.get_minutes() << setfill(' ') << "\n"; cout << "Try again? (y/n) "; string input;getline(cin, input);if (input != "y") more = false; }return 0; } #include <iostream>#include <iomanip>#include <string>usingnamespace std;#include "ccc_time.h"#include “Clock.h”
Derived Classes • The Clock class presented in clocks1.cpp provides a base class that can tell the current local time. • You can set a clock to either "military" or "am/pm" format. • We will create a derived class, TravelClock that gains two additional data fields: location and time_difference.
Derived Classes (cont.) • The time for a TravelClock is computed by taking the local time and adding the time difference. TravelClock clock("New York", 3); cout << "The time in " << clock.get_location() << " is " << clock.get_hours() << ":" << clock.get_minutes();
Derived Class (cont.) • A TravelClock differs from a Clock in three ways: • Its objects store the location and time difference. • The get_hours function of the TravelClock adds the time difference to the current time. • The get_location function returns the actual location, not the string "Local". • When the TravelClock class inherits from the Clock class, it needs only to spell out these three differences. • Specification of class TravelClock follows. class TravelClock : public Clock {public: TravelClock(bool mil, string loc, double off); int get_hours() const; string get_location() const;private: string location; int time_difference;};
Calling the Base-Class Constructor • The constructor of a derived class has two tasks: • Initialize the base object. • Initialize all data members. • Frequently, a derived-class constructor must invoke the base-class constructor before initializing the derived-class data.
Calling the Base-Class Constructor (cont.) • Here, we call the Clock constructor with the parameter mil before executing the code inside the {}. • TravelClock::TravelClock(bool mil, string loc, int diff) : Clock(mil) { • location = loc; • time_difference = diff; • while (time_difference < 0) • time_difference = time_difference + 24; • } • If you omit the base-class constructor, then the base object • is constructed with the default constructor of the base class. • If the base class has no default constructor, then you have to • explicitly call the base-class constructor in the derived-class • constructor.
Calling the Base-Class Constructor (Syntax Constructor with Base-Class Initializer) • Constructor with Base-Class Initializer Derived_class_name::Derived_class_name(parameter list) :Base_class_name(expressions) { … statements inside the constructor … } • Example: Manager::Manager(string name, double salary, string dept) : Employee(name, salary) { department = dept; } • Purpose: Supply the implementation of a constructor, initializing the base class before the body of the derived-class constructor.
Calling Base-Class Member Functions • Suppose B::f is a function in the base class. The the derived class D can take three kinds of actions. • The derived class can extendB::f by supplying a new implementation that refers to the base-class implementation. • For example, the TravelClock::get_hours function is an extension of Clock::get_hours. • The derived class can replaceB::f by supplying a new implementation that is unrelated to the base-class implementation. • For example, TravelClock::get_location is a replacement for Clock::get_location. • The derived class can inheritB::f by not supplying an implementation for f. • For example, TravelClock inherits Clock::get_minutes and Clock::is_military.
Calling Base-Class Member Functions • A member function called without a parameter is called through the implicit parameter object of the derived class. if (is_military()) //it uses military time return (h + time_difference) % 24; • When the base class and the derived class have a member function with the same name, you must be more specific which function you want to call. int TravelClock::get_hours() const { int h = Clock::get_hours(); . . . } • The following call would die in an infinite recursion. int TravelClock::get_hours() const { int h = get_hours(); . . . } calls base-class function calls itself!! Not correct in this case!!!
Example: Specification and Implementation of class TravelClock class TravelClock : public Clock {public:/** Constructs a travel clock that can tell the time at a specified location @param mil true if the clock uses military format @param loc the location @param diff the time difference from the local time */TravelClock(bool mil, string loc, int diff); string get_location() const; int get_hours() const; private: string location; int time_difference; };
Implementation of class TravelClock TravelClock::TravelClock(bool mil, string loc, int diff) : Clock(mil) { location = loc; time_difference = diff;while (time_difference < 0) time_difference = time_difference + 24; } string TravelClock::get_location() const {return location; } int TravelClock::get_hours() const { int h = Clock::get_hours();if (is_military())return (h + time_difference) % 24;else { h = (h + time_difference) % 12;if (h == 0) return 12;elsereturn h; } }
Example of program using the TravelClock class … main function of program Clocks2.cpp int main() { Clock clock1(true); TravelClock clock2(true, "Rome", 9); TravelClock clock3(false, "Tokyo", -7); cout << clock1.get_location() << " time: " << clock1.get_hours() << ":" << setw(2) << setfill('0') << clock1.get_minutes() << setfill(' ') << "\n"; cout << clock2.get_location() << " time: " << clock2.get_hours() << ":" << setw(2) << setfill('0') << clock2.get_minutes() << setfill(' ') << "\n"; cout << clock3.get_location() << " time: " << clock3.get_hours() << ":" << setw(2) << setfill('0') << clock3.get_minutes() << setfill(' ') << "\n";return 0; }
Polymorphism • You will find that there is quite a bit of repetitive code in clocks2.cpp. • It would be nicer if all three clocks were collected in a vector, as attempted next: vector<Clock> clocks(3); clocks[0] = Clock(true); clocks[1] = TravelClock(true, "Rome", 9); clocks[2] = TravelClock(false, "Tokyo", -7); for (int i = 0; i < clocks.size(); i++) { cout << clocks[i].get_location() << " time: " << clocks[i].get_hours() << ":" << setw(2) << setfill('0' << clocks[i].get_minutes() << setfill(' ') << "\n"; } • Unfortunately, this does not work as expected. The output of this program is: Local time is 21:15 Local time is 21:15 Local time is 21:15 This is valid since every object of type TravelClock is also of type Clock
Polymorphism • The compiler realizes that a TravelClock is a special case of a Clock, and permits the assignment clocks[1] = TravelClock(true, "Rome", 9); • However, there is no room to store the derived-class data. • That data simply gets sliced away when a derived-class object is assigned to a base-class variable. • The following figure illustrates the area that is copied with the previous statement. This portion of a TravelClock is copied to the Clock object in the previous assignment statement.
Polymorphism (cont.) • A base-class pointer can point to a derived class object. (The reverse is an error). • By creating a vector of pointers to the base-class type, we can hold a collection of both base-class and derived-class objects. vector<Clock*> clocks(3); // populate clocks clocks[0] = new Clock(true); clocks[1] = new TravelClock(true, "Rome", 9); clocks[2] = new TravelClock(false, "Tokyo", -7); • Pointers to the various clock objects all have the same size - the size of a memory address - even though the objects themselves may have different sizes. • Since every TravelClock is a special case of a Clock, the starting address of a TravelClock object is the starting address of a Clock object.
Polimorphisms (cont.) A pointer to Clock is also a valid pointer to objects of type TravelClock. This figure illustrates the memory organization after the three assignment statements in the code.
Polymorphism • Unfortunately, the following code does not work either! cout << clocks[i]->get_location() << " time: " << clocks[i]->get_hours() << ":" << setw(2) << setfill('0') << clocks[i]->get_minutes() << setfill(' ') << "\n"; • The output is still: Local time is 21:15 Local time is 21:15 Local time is 9:15 • The reason is that both, Clock and TravelClock, have a get_location and a get_hours member function. • The compiler calls the Clock member function because the pointer is of type Clock*. But the correct result should be: Local time: 11:53 Rome time: 20:53 Tokyo time: 4:53
Polymorphism (cont.) • You must alert the compiler that the function call needs to be preceded by the appropriate function selection, which can be a different one for every iteration in the loop. • Such a selection/call combination is called dynamic binding. • The traditional call, which always invokes the same function, is called static binding. • To tell the C++ compiler that a particular function needs to be bound dynamically, the function must be tagged as virtual. class Clock { public: Clock(bool use_military); virtual string get_location() const; virtual int get_hours() const; int get_minutes() const; bool is_military() const; private: . . . }; With this, the previous code works as expected.
Polymorphism - Virtual Function Definition • Syntax of Virtual Function Definition • class Class_name { • virtual return_type function_name(parameter list); • . . . • }; • Example: • class Employee { • public: • virtual double get_salary(); • . . . • }; • Purpose: Define a dynamically bound function that can be • redefined in derived classes. When the function is called, • the actual type of the implicit parameter determines which • version of the function to execute.