490 likes | 526 Views
CMSC 202. Inheritance. “is-a”. A car is a vehicle A sorted list is a list A pump is a piece of equipment A student is a person A professor is a faculty member A lecturer is a faculty member. More “is-a”. Many objects have an “is-a” relationship with another object.
E N D
CMSC 202 Inheritance
“is-a” • A car is a vehicle • A sorted list is a list • A pump is a piece of equipment • A student is a person • A professor is a faculty member • A lecturer is a faculty member
More “is-a” • Many objects have an “is-a” relationship with another object. • One object is more a more specialized version of another • This “is-a” relationship is represented in OOP by inheritance.
UMBCCOMMUNITYMEMBER STUDENT FACULTY MEMBER GRADUATESTUDENT UNDERGRADSTUDENT PROFESSOR LECTURER An inheritance hierarchy
Inheritance Hierarchy • In an inheritance hierarchy, objects at the top levels are more general in nature than the ones on the lower levels. • The lower you go in the hierarchy, the more specialized the objects become • Inheritance is transitive across levels • A “graduate student” is a “student” • A “student” is a “UMBC community member” • Therefore a “graduate student” is also a “UMBC community member”
Inheritance and Classes • The more general object is a referred to as a “base class”. • The more specialized object is a “derived class”. • The base class contains all that’s common among the derived classes. • The attributes (data) and behavior (methods) of the base class are inherited (shared) by the derived class. • The derived class extends or modifies the base class behavior.
Inheritance, Generalizationand Specialization • Inheritance is the mechanism for sharing attributes and operations among classes • Generalization and Specialization are two viewpoints of the is-a relationship. • The base class can be considered a generalization of of the derived class • More commonly, the derived class is considered a specialization of the base class
“is-a” vs. “has-a” vs. “uses-a” • Inheritance is the mechanism for implementing the “is-a” relationship between objects. • Composition/Aggregation is the mechanism for implementing the “has-a” relationship between objects (like Stack and List in project 4). • An object uses another object by calling a public method of that object.
Inheritance and OOP • Inheritance is an abstraction for sharing similarities among classes while preserving their differences • Inheritance allows us to group classes into families of related types, allowing for the sharing of common operations and data • Inheritance of operations promotes code reuse
Inheritance in C++ • C++ provides three methods for inheritance • “public” inheritance is used to implement the “is-a” relationship • “private” inheritance is use to implement the “is-implemented-in-terms-of” relationship • No one knows what “protected” inheritance is • Our discussions are limited to PUBLIC inheritance
Public inheritance means “is-a” If you write that class D (“Derived”) publicly inherits from class B (“Base”), then you are telling C++ compilers (and human readers of your code) that every D is a B, but notvice-versa. You are saying that B represents a more general concept than D, that D represents a more specialized concept than B. You are asserting that anywhere an object of type B can be used, an object of type D can be used just as well, because every object of type D is an object of type B. On the other hand, if you need an object of type D, and object of type B will not do; every D is a B, but not vice-versa. -- “Effective C++ Second Edition”, page 155
Make sure inheritance models “is-a” Is a square a rectangle? Absolutely. We all know that a square is a rectangle whose length and width are the same. Should we model a square by publicly inheriting from rectangle? A good discussion we’ll have.
“is-a” vs. “has-a” revisited The textbook uses an example in which a Circle class inherits from a Point class. What do you think of this?
A Simple Class Example // a military time class class Time { public: Time(int h = 0, int m = 0, int s = 0); void setTime (int h, int m, int s); void increment( ); void printTime ( ) const; private: int hrs; // 0 - 23 int mins; int secs; };
Some Time Methods Time::Time (int initHrs, int initMins, int initSecs) : hrs (initHrs), mins (initMins), secs (initSecs) { // no code – uses member initialization list } void Time::setTime (int hours, int minutes, int seconds) { hrs = hours; mins = minutes; secs = seconds; }
Time::Increment void Time::increment ( ) { secs++; if (secs > 59) { secs = 0; mins++; if (mins > 59) { mins = 0; hrs++; if (hrs > 23) hrs = 0; } } }
Time::PrintTime // print time as hh:mm:ss with leading zeroes void Time::printTime ( ) const { if (hrs < 10) cout << '0'; cout << hrs << ':'; if (mins < 10) cout << '0'; cout << mins << ':'; if (secs < 10) cout << '0'; cout << secs; }
An extended time class // ExtTime publicly inherits (is derived from) Timeclass ExtTime: public Time { public: enum ZoneType = {EST, CST, MST, PST, EDT, CDT, MDT, PDT}; ExtTime(int h = 0, int m = 0, int s = 0, ZoneType z = EST); void setExtTime (int h, int m, int s, ZoneType z); void printExtTime( ) const; private: ZoneType zone; };
What does this mean? • ExtTime publicly inherits from Time. • Time is the base class. • ExtTime is the derived class. • All of Time’s public methods are available to the ExtTime class and to users of the ExtTime class • ExtTime extends Time by adding the time zone; an example of specialization • ExtTime DOES NOT have access to Time’s private members
An Aside -- Protected Access // A base class can give a derived class direct access to // some members by placing them in the protected section // only derived classes (and their friends) can access // protected membersclass Time { public: Time(int h = 0, int m = 0, int s = 0); void setTime(int h, int m, int s); void increment( ); void printTime( ) const; protected: // potential ENCAPSULATION VIOLATION! int hrs, mins, secs;};
ExtTime constructor // the ExtTime constructor calls the Time (base class) // constructor using the member initialization list // Without this, Time’s default constructor would be called// Base class constructors are called first // ExtTime then initializes its own data member ExtTime::ExtTime (int Hrs, int Mins, int Secs, ZoneType Zone) : Time (Hrs, Mins, Secs) { zone = Zone; }
ExtTime::setExtTime // SetExtTime calls Time::SetTime to set // (private) hours, mins and secs inherited from Time void ExtTime::setExtTime(int h, int m, int s, ZoneType z) { SetTime (h, m, s); zone = z; }
ExtTime::printExtTime void ExtTime::printExtTime ( ) const { static char* zoneString[8] = { “EST", "CST", "MST", "PST", "EDT", "CDT", "MDT", "PDT" }; printTime ( ); // a base class method cout << ' ' << zoneString[zone]; }
Using ExtTime main ( ) { ExtTime eTime (12, 0, 0); // noon, EST eTime.printExtTime ( ); // ExtTime method eTime.increment ( ); // an inherited method eTime.printTime ( ); // just prints hh:mm:ss eTime.setExtTime (13, 12, 7, ExtTime::PST); eTime.printExtTime ( ); // ExtTime method }
Assignment operator • The base class assignment operator (operator=) is not inherited and NOT called automatically from the derived class assignment operator
Time::operator= Time& Time::operator= (const Time& rhs) {if (this != &rhs){ hrs = rhs.hrs; mins = rhs.mins; secs = rhs.secs; } return *this; }
Erroneous ExtTime::operator= ExtTime& ExtTime:: operator=(const ExtTime &rhs) {if (this != &rhs) zone = rhs.zone; return *this; }
What happens? ExtTime eTime1 (12, 30, 0, ExtTime::EST); ExtTime eTime2 (13, 45, 30, ExtTime::PDT); // assignment only changes the zone eTime2 = eTime1; // eTime2 is 13:45:30 EST
Correct ExtTime::operator= ExtTime& ExtTime::operator=(const ExtTime &rhs){ if (this != &rhs) { // explicitly call base class op= Time::operator= (rhs); zone = rhs.zone; } return *this;}
Other Notes on Inheritance • Base class constructors and destructors are NOT inherited • The base class constructor (destructor) will automatically be invoked when the derived class is constructed (destroyed) • The base class constructor will be called first • The derived class destructor will be called first (objects are destroyed in the reverse order they are constructed) • Base class operator= is NOT inherited and NOT automatically called by derived class operator= • “friendship” is NOT inherited. I.e. a friend of the base class is not automatically a friend of the derived class
Inheritance vs. Templates • A stack must be homogeneous because it only holds objects of one kind a time. So if we want several kinds of stacks, we need several kinds of classes. • Now consider a problem that deals with cats. Each breed of cat is slightly different, from the others so you’ll need separate classes. • These problems sound similar, yet result in different software designs “Effective C++, Second Edition”, item 41
Stacks and Cats • The difference is in the relationship the behavior and the type of object being manipulated. • We have many types of both stacks and cats. • As we’ve seen the behavior of a stack is NOT affected by the type of object stored in it and was implemented as a template. • Different cats, on the other hand, behave differently depending on what type of cat they are. So you can’t just write one class to handle every kind of cat – template won’t work.. We need inheritance
Inheritance vs. Templatessummary • A template should be used to generate a collection of classes when the type of object does not affect the behavior of the class’s functions • Inheritance should be for a collection of classes when the type of object does affect the behavior of the class’s functions.
Specialization by overriding functions • An inheritance hierarchy shows how more specialized classes are derived from more general classes. • One primary mechanism of this specialization is overriding functions (methods) • Overriding a method in the derived class means defining a method with the exact same signature as a method in the base class. The derived class method “hides” the base class method from the user
Overriding is NOT overloading • Recall that overloading a function meant using the same function name, but with different parameters. • Overriding a function means redefining a function using the same name and parameters. This is possible only with inheritance.
Going back in Time In our Time/ExtTime example, ExtTime extended time by adding new methods and data members. But suppose ExtTime was defined a little differently. Suppose instead of having printExtTime( ), the ExtTime class redefined printTime( ).
Revise ExtTime class with function overriding // ExtTime overrides Time’s printTime( ) methodclass ExtTime: public Time { public: enum ZoneType = {EST, CST, MST, PST, EDT, CDT, MDT, PDT}; ExtTime(int h = 0, int m = 0, int s = 0, ZoneType z = EST); void setExtTime (int h, int m, int s, ZoneType z);void printTime( ) const; // function OVERRIDING private: ZoneType zone; };
ExtTime::printTime( ) void ExtTime::printTime ( ) const {// new code for printing hours, mins, secs // or call Time::printTime( ) // then output the zone }
// main can’t call Time::printTime( ) because it’s// lexically “hidden” behind ExtTime::printTime( ) main ( ) { ExtTime eTime (12, 0, 0); // noon, EST eTime.increment ( ); // an inherited method eTime.printTime ( ); // overridden method }
Interface vs.. Implementation • A function’s interface is just its prototype (declaration) • A function’s implementation is the code inside the function (the definition)
Inheriting only the interface • Sometimes you want derived classes to inherit only the interface of a member function. • C++ provides this capability with “pure virtual” functions in the base class virtual void draw ( ) const = 0;
Inheriting the interface and the ability to override the implementation • Sometimes you want derived classes to inherit both the interface and the implementation, but allow them to override the default implementation the base class provides • C++ provides this capability with (impure?) virtual functions in the base class virtual void error (const string & msg);
Inheriting the interface and an unchangeable implementation • Sometimes you derived classes to inherit both the interface and the implementation without allowing them to override anything. • C++ provides this with nonvirtual functions in the base class int objectID ( ) const;
A Shape example class Shape { // a base classpublic: virtual void draw( ) const = 0; // pure virtual function virtual void error (const string& msg); // virtual function int objectID ( ) const; // nonvirtual function . . . . . . }; // some derived classes class Rectangle : public Shape { . . . . . }; class Ellipse : public Shape { . . . . . }; See “Effective C++ Second Edition”, Item 36
draw( ) • draw( ) is a pure virtual function. It’s presence means that Shape is an “abstract” class and cannot be instantiated. • The purpose of a pure virtual function is to have derived classes inherit the interface only • It’s reasonable for all shapes to be drawable, but the Shape class can provide no reasonable default implementation. • We are saying to designers of derived classes –“You must provide a draw function, but I have no idea how you’re going to implement it.”
error ( ) • Error( ) is a virtual function. • Virtual functions provides an interface and a defaultimplementation which the derived class is at liberty to override. • Shape::error( ) says that every Shape must support a function named error() and each derived class is free to handle the error in anyway it sees fit. If the derived class doesn’t want to do anything special, it can fall back on the default behavior provided by the Shape class.
objectID ( ) • objectID( ) is a nonvirtual function. • Nonvirtual functions are provided in the base class so that derived classes inherit a function’s interface as well as a mandatoryimplementation • A nonvirtual function provides an invariantoverspecialization because it specifies behavior that is not supposed to change.
Two important points about interfaces and implementation • Interfaces are always inherited. Since public inheritance means “is-a” anything that is true of the base class is also true of it’s derived classes, so if a function applies to the class, it must also apply to its subclasses. • Never override a nonvirtual function. Theoretically, nonvirtual functions provide a mandatory implementation so should not be redefined. Pragmatically, you’re code won’t always work the way you want. (more on this later).
More notes on interface/implementation • Any class that defines one or more (pure) virtual function should define a virtual destructor (more later) • It’s sometimes useful to declare a class containing nothing but pure virtual functions. Such a class is called a “protocol class” and provides only function interfaces for derived classes.