Top Banner
CMSC 202 Polymorphism
30

CMSC 202

Jan 14, 2016

Download

Documents

fathi

CMSC 202. Polymorphism. 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. - PowerPoint PPT Presentation
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: CMSC 202

CMSC 202

Polymorphism

Page 2: CMSC 202

2

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.

Page 3: CMSC 202

3

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)

Page 4: CMSC 202

4

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

Page 5: CMSC 202

5

Static vs. Dynamic Binding

• Binding

The determination of which method in the classhierarchy is to be invoked for a particular object.

• Static (Early) Binding occurs at compile time

When the compiler can determine which method in theclass hierarchy to use for a particular object.

• Dynamic (Late) Binding occurs at run time

When the determination of which method in the classhierarchy to use for a particular object occurs duringprogram execution.

Page 6: CMSC 202

6

Static Binding

For example,

Time t1; ExtTime et2;

t1.setTime(12, 30, 00); // static binding et1.setExtTime(13, 45, 30); // static binding

t1.printTime( ); // static binding – Time’s printTime( ) et1.printTime(); // static binding – ExtTime’s printTime( )

Page 7: CMSC 202

7

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

Page 8: CMSC 202

8

A common exampleclass Shape { public:

virtual void draw ( ) const = 0; // pure virtualvirtual void error ( ); // virtualvoid objectID ( ); // non virtual

};

void Shape::error ( ){ cerr << “Shape error” << endl;}

void objectID ( ){ cout << “a shape” << endl;}

Page 9: CMSC 202

9

class Circle : public Shape{ public:

virtual void draw( ) const; // method for drawing a circlevirtual void error ( ); // overriding Shape::error…

};

void Circle::draw ( ) const{ // code for drawing a circle}

void Circle::error ( ) { cout << “Circle error” << endl;}

Page 10: CMSC 202

10

class Rectangle : public Shape{ public:

virtual void draw( ) const; // method for drawing a rectangle…

};

void Rectangle::draw ( ) const{ // code to draw a rectangle}

Page 11: CMSC 202

11

Now consider these pointers: Shape *pShape; Circle *pCircle = new Circle; Rectangle *pRectangle = 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.

pShape = pCircle; // pShape’s dynamic type is now Circle pShape->draw( ); // calls Circle::draw and draws a circle

pShape = pRectangle; // pShape’s dynamic type is Rectangle pShape->draw ( ); // calls Rectangle::draw

Page 12: CMSC 202

12

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( );

Page 13: CMSC 202

13

A linked list of base class pointers

LinkedList<Shape * > shapes;

shapes.insert (new Circle);

shapes.insert (new Rectangle);

shapes.insert (new Triangle);

// traverse the list, printing each Shape in list

Page 14: CMSC 202

14

DOG WHALECAT

ANIMAL

A simple ANIMAL hierarchy

Page 15: CMSC 202

15

class Animal { public:

Animal ( ) {name = “no name”, nrLegs = 0;}Animal ( string s, int L) : name (s), nrLegs(L) { }virtual ~Animal ( );virtual void speak ( ) const { cout << “Hi, Bob”; }string getName (void) const { return name; }void setName (string s) { name = s; }void setNrLegs (int legs) { nrLegs = legs;}int getNrLegs ( void ) const { return nrLegs;}

private:string name;int nrLegs;

};

Page 16: CMSC 202

16

class Dog : public Animal{

public:Dog ( ) : Animal (“dog”, 4), breed (“dog”) { }Dog (string s1, strings2) : Animal (s1, 4), breed (s2) { }~Dog ( );void setBreed (string s1) { breed = s1; }string getBreed (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;

};

Page 17: CMSC 202

17

class Whale : public Animal{

public:Whale ( );~Whale ( );

private:};

Page 18: CMSC 202

18

main ( ) { Animal anAnimal (“Homer”, 2); Dog aDog (“Fido”, “mixed”); Whale aWhale (“Orka”); anAnimal.speak( ); aDog.speak( ); aDog.speak( 4 ); aWhale.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]->getName( ) << “ says: “ zoo[a] -> speak( );

}}

Page 19: CMSC 202

19

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.

Page 20: CMSC 202

20

drawShape( ) with a pointervoid drawShape ( Shape *sp){ cout << “I am “ ; sp -> objectID ( ); sp -> draw ( ); if (something bad)

sp->error ( );

}

What is the output if drawShape( ) is passeda pointer to a Circle?a pointer to a Rectangle?

Page 21: CMSC 202

21

drawShape ( ) via reference

void drawShape ( Shape& shape){ cout << “I am “ ; shape.objectID ( ); shape.draw ( ); if (something bad)

shape.error ( );

}

What is the output if drawShape is passeda reference to a Circle?a reference to a Rectangle?

Page 22: CMSC 202

22

Calling virtual methods from within other methods

• Suppose virtual drawMe( ) is added to Shape and inherited by Rectangle without being overridden.

void Shape::drawMe ( void){

cout << “drawing: “ << endl;draw( );

}

Page 23: CMSC 202

23

Which draw( ) gets called

From within drawMe( ) with this code?

Rectangle r1;

r1.drawMe ( );

The Rectangle version of draw( ), even though drawMe( ) was only defined in Shape. Why?

Because inside of drawMe( ), the call to draw( ) is really this->draw ( ); and since a pointer is used, we get the desired polymorphic behavior.

Page 24: CMSC 202

24

Don’t redefine objectID( )Note that neither Circle nor Rectangle redefined the objectID( ) method which was a nonvirtual function. But what if they had?

Suppose the Rectangle class had it’s own version of objectID().Consider this code:

Rectangle R; // a Rectangle objectShape *pS = &R; // base class pointer to a RectangleRectangle *pR = &R; // derived class pointer to a Rectangle

what is the output of the following?pS -> objectID ( );pR -> objectID ( );

Bottom Line – don’t override nonvirtual functions

Page 25: CMSC 202

25

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.

Page 26: CMSC 202

26

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”.

Page 27: CMSC 202

27

The same is true for assignment

Time t(10, 5, 0);ExtTime et(20, 15, 10, ExtTime::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

Page 28: CMSC 202

28

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 ??

Page 29: CMSC 202

29

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

Page 30: CMSC 202

30

Designing A Base Class with Inheritance and Polymorphism

in mindFor the base class

1. Identify the set of operations common to all the children

2. Identify which operations are type-independent (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)