CMSC 202 Polymorphism Inheritance and Polymorphism Inheritance allows

  • Slides: 30
Download presentation
CMSC 202 Polymorphism

CMSC 202 Polymorphism

Inheritance and Polymorphism • Inheritance allows us to define a family of classes that

Inheritance and Polymorphism • Inheritance allows us to define a family of classes that share a common interface. • Polymorphism allows us to manipulate objects of these classes in a type-independent way. • We program the common interface through a pointer or reference to an (abstract) base class. The actual operation to invoke is not determined until run-time based on the specific type of object actually addressed. 2

C++ and Polymorphism • Polymorphism is the ability of a base class pointer (or

C++ and Polymorphism • Polymorphism is the ability of a base class pointer (or reference) to refer transparently to any of its derived classes. • Polymorphism (and dynamic binding) are supported only when we use pointers (or references) 3

Inheritance and Pointers • A base class pointer can point to an object of

Inheritance and Pointers • A base class pointer can point to an object of a derived class – Derived class object “is-a” base class object – But can’t use pointer to call methods only defined in the derived class. • A derived class pointer CANNOT point to the base class – The base class doesn’t have any of the extensions provided by the derived class 4

Static vs. Dynamic Binding • Binding The determination of which method in the class

Static vs. Dynamic Binding • Binding The determination of which method in the class hierarchy is to be invoked for a particular object. • Static (Early) Binding occurs at compile time When the compiler can determine which method in the class hierarchy to use for a particular object. • Dynamic (Late) Binding occurs at run time When the determination of which method in the class hierarchy to use for a particular object occurs during program execution. 5

Static Binding For example, Time t 1; Ext. Time et 2; t 1. set.

Static Binding For example, Time t 1; Ext. Time et 2; t 1. set. Time(12, 30, 00); et 1. set. Ext. Time(13, 45, 30); t 1. print. Time( ); et 1. print. Time(); // static binding – Time’s print. Time( ) // static binding – Ext. Time’s print. Time( ) 6

Dynamic Binding • Compiler cannot determine binding of object to method • Binding is

Dynamic Binding • Compiler cannot determine binding of object to method • Binding is determined dynamically at runtime • To indicate that a method is to be bound dynamically, the base class must use the reserved word virtual • When a method is defined as virtual, all overriding methods from that point on down the hierarchy are virtual, even if not explicitly defined to be so 7

A common example class Shape { public: virtual void draw ( ) const =

A common example class Shape { public: virtual void draw ( ) const = 0; virtual void error ( ); void object. ID ( ); }; // pure virtual // non virtual void Shape: : error ( ) { cerr << “Shape error” << endl; } void object. ID ( ) { cout << “a shape” << endl; } 8

class Circle : public Shape { public: virtual void draw( ) const; virtual void

class Circle : public Shape { public: virtual void draw( ) const; virtual void error ( ); … // method for drawing a circle // overriding Shape: : error }; void Circle: : draw ( ) const { // code for drawing a circle } void Circle: : error ( ) { cout << “Circle error” << endl; } 9

class Rectangle : public Shape { public: virtual void draw( ) const; … };

class Rectangle : public Shape { public: virtual void draw( ) const; … }; // method for drawing a rectangle void Rectangle: : draw ( ) const { // code to draw a rectangle } 10

Now consider these pointers: Shape *p. Shape; Circle *p. Circle = new Circle; Rectangle

Now consider these pointers: Shape *p. Shape; Circle *p. Circle = new Circle; Rectangle *p. Rectangle = new Rectangle; Each pointer has static type based on the way it’s defined in the program text. The pointer’s dynamic type is determined by the type of object to which they currently refer. p. Shape = p. Circle; p. Shape->draw( ); // p. Shape’s dynamic type is now Circle // calls Circle: : draw and draws a circle p. Shape = p. Rectangle; // p. Shape’s dynamic type is Rectangle p. Shape->draw ( ); // calls Rectangle: : draw 11

An array of base class pointers Shape *shapes[3]; shapes[0] = new Circle; shapes[1] =

An array of base class pointers Shape *shapes[3]; shapes[0] = new Circle; shapes[1] = new Rectangle; shapes[2] = new Triangle; for (int s = 0; s < 3; s++) shapes[s]->draw( ); 12

A linked list of base class pointers Linked. List<Shape * > shapes; shapes. insert

A linked list of base class pointers Linked. List<Shape * > shapes; shapes. insert (new Circle); shapes. insert (new Rectangle); shapes. insert (new Triangle); // traverse the list, printing each Shape in list 13

ANIMAL DOG CAT WHALE A simple ANIMAL hierarchy 14

ANIMAL DOG CAT WHALE A simple ANIMAL hierarchy 14

class Animal { public: Animal ( ) {name = “no name”, nr. Legs =

class Animal { public: Animal ( ) {name = “no name”, nr. Legs = 0; } Animal ( string s, int L) : name (s), nr. Legs(L) { } virtual ~Animal ( ); virtual void speak ( ) const { cout << “Hi, Bob”; } string get. Name (void) const { return name; } void set. Name (string s) { name = s; } void set. Nr. Legs (int legs) { nr. Legs = legs; } int get. Nr. Legs ( void ) const { return nr. Legs; } private: string name; int nr. Legs; }; 15

class Dog : public Animal { public: Dog ( ) : Animal (“dog”, 4),

class Dog : public Animal { public: Dog ( ) : Animal (“dog”, 4), breed (“dog”) { } Dog (string s 1, strings 2) : Animal (s 1, 4), breed (s 2) { } ~Dog ( ); void set. Breed (string s 1) { breed = s 1; } string get. Breed (void ) const { return breed; } void speak ( ) const { cout << “Bow Wow ”; } void speak (int n) const { for (int j = 0; j < n: j++) speak ( ); }. . . private: string breed; }; 16

class Whale : public Animal { public: Whale ( ); ~Whale ( ); private:

class Whale : public Animal { public: Whale ( ); ~Whale ( ); private: }; 17

main ( ) { Animal an. Animal (“Homer”, 2); Dog a. Dog (“Fido”, “mixed”);

main ( ) { Animal an. Animal (“Homer”, 2); Dog a. Dog (“Fido”, “mixed”); Whale a. Whale (“Orka”); an. Animal. speak( ); a. Dog. speak( 4 ); a. Whale. speak( ) ; Animal *zoo[ 3 ]; zoo[0] = new Whale; zoo[1] = new Animal; zoo[2] = new Dog (“Max”, “Terrier”); for (int a = 0; a < 3; a++) { cout << zoo[a]->get. Name( ) << “ says: “ zoo[a] -> speak( ); } } 18

Works for functions too • Perhaps the most important use of polymorphism is the

Works for functions too • Perhaps the most important use of polymorphism is the writing of functions to deal with all classes in the inheritance hierarchy. This is accomplished by defining the function parameters as pointers (or references) to base class objects, then having the caller pass in a pointer (or reference) to a derived class object. Dynamic binding calls the appropriate method. • These functions are often referred to as “polymorphic” functions. 19

draw. Shape( ) with a pointer void draw. Shape ( Shape *sp) { cout

draw. Shape( ) with a pointer void draw. Shape ( Shape *sp) { cout << “I am “ ; sp -> object. ID ( ); sp -> draw ( ); if (something bad) sp->error ( ); } What is the output if draw. Shape( ) is passed a pointer to a Circle? a pointer to a Rectangle? 20

draw. Shape ( ) via reference void draw. Shape ( Shape& shape) { cout

draw. Shape ( ) via reference void draw. Shape ( Shape& shape) { cout << “I am “ ; shape. object. ID ( ); shape. draw ( ); if (something bad) shape. error ( ); } What is the output if draw. Shape is passed a reference to a Circle? a reference to a Rectangle? 21

Calling virtual methods from within other methods • Suppose virtual draw. Me( ) is

Calling virtual methods from within other methods • Suppose virtual draw. Me( ) is added to Shape and inherited by Rectangle without being overridden. void Shape: : draw. Me ( void) { cout << “drawing: “ << endl; draw( ); } 22

Which draw( ) gets called From within draw. Me( ) with this code? Rectangle

Which draw( ) gets called From within draw. Me( ) with this code? Rectangle r 1; r 1. draw. Me ( ); The Rectangle version of draw( ), even though draw. Me( ) was only defined in Shape. Why? Because inside of draw. Me( ), the call to draw( ) is really this->draw ( ); and since a pointer is used, we get the desired polymorphic behavior. 23

Don’t redefine object. ID( ) Note that neither Circle nor Rectangle redefined the object.

Don’t redefine object. ID( ) Note that neither Circle nor Rectangle redefined the object. ID( ) method which was a nonvirtual function. But what if they had? Suppose the Rectangle class had it’s own version of object. ID(). Consider this code: Rectangle R; // a Rectangle object Shape *p. S = &R; // base class pointer to a Rectangle *p. R = &R; // derived class pointer to a Rectangle what is the output of the following? p. S -> object. ID ( ); p. R -> object. ID ( ); Bottom Line – don’t override nonvirtual functions 24

Old code and new code • In the procedural world (like that of C),

Old code and new code • In the procedural world (like that of C), it’s easy for new code to call old code using functions. However, it’s difficult for old code to call new code unless it’s modified to know about the new code. • In the OO world, old code (polymorphic functions) can dynamically bind to new code. This occurs when a new derived class is passed (by pointer or reference) to the polymorphic function. The polymorphic function needs no modification. 25

Don’t Pass by Value A function which has a base class parameter passed by

Don’t Pass by Value A function which has a base class parameter passed by value should only be used with base class objects – for two reasons 1. The function isn’t polymorphic. Polymorphism only occurs with parameters passed by pointer or reference 2. Even though a derived class object can be passed to such a function (a D is-a B), none of the derived class methods or data members can be used in that function. This is a phenomenon called “member slicing”. 26

The same is true for assignment Time t(10, 5, 0); Ext. Time et(20, 15,

The same is true for assignment Time t(10, 5, 0); Ext. Time et(20, 15, 10, Ext. Time: : EST); • A derived class object can be assigned to a base class object t = et; • // legal, BUT Member slicing occurs. A base class object cannot be assigned to a derived class object et = t; // illegal! Not all data members can be filled 27

Polymorphism and Destructors • A problem – if an object (with a non-virtual destructor)

Polymorphism and Destructors • A problem – if an object (with a non-virtual destructor) is explicitly destroyed by applying the delete operator to a base class pointer, the base class destructor is invoked. Circle C; Shape *sp = &C; delete sp; // calls Shape’s destructor // if the destructor is not // virtual • So what ? ? 28

Polymorphism and Destructors • Solution -- Declare a virtual destructor for any base class

Polymorphism and Destructors • Solution -- Declare a virtual destructor for any base class with at least one virtual function. • Then, when the derived class’s destructor is invoked, the base class destructor will also be invoked automatically 29

Designing A Base Class with Inheritance and Polymorphism in mind For the base class

Designing A Base Class with Inheritance and Polymorphism in mind For the base class 1. Identify the set of operations common to all the children 2. Identify which operations are typeindependent (these become (pure) virtual to be overridden in derived classes) 3. Identify the access level (public, private, protected) of each operation For a more complete discussion, see “Essential C++”, Stanley Lippman (section 5. 4) 30