CSE 143 Substituting Derived Classes • Recall that an instance of a derived class can always //client function (not a method) be substituted for an Dynamic Dispatch and Virtual void printPoint( Point pt ) instance of a base class { pt.print( cout ); • Derived class guaranteed to Functions //the question: which print? have (at least) the same data } and interface as base class • But you may not get the Point p( 1.0, 9.0 ); behaviour you want! ColorPoint cp( 6.0, 7.0, red ); [Chapter 8 pp.354-370] printPoint( p ); p = cp; //information lost printPoint( p ); printPoint( cp ); 07/22/01 07/22/01 R-1 R-2 Pointers And Inheritance Static And Dynamic Types • You can also substitute a • In C++, every variable has a static and a dynamic pointer to a derived class type //client function for a pointer to a base void printPoint( Point *ptr ) • Static type is declared type of variable class { Every variable has a single static type that never changes ofstream ofs( "point.out" ); • There’s still that guarantee ptr->print( ofs ); • Dynamic type is type of object the variable actually about data and interface ofs.close(); contains or refers to } • Also holds for reference types Dynamic type can change during the program! • No information disappears!! Point *pptr = new Point( 1.0, 9.0 ); • Up to now, these have always been identical ColorPoint *cpptr = • Unfortunately, we still have new ColorPoint( 6.0, 7.0, red ); the same problems… • But not any more! printPoint( pptr ); printPoint( cpptr ); Point *myPointPointer = new ColorPoint( 3.14, 2.78, green ); pptr = cpptr; printPoint ( pptr); 07/22/01 07/22/01 R-3 R-4 "Dispatch" Dynamic Dispatch • "Dispatching" is the act of deciding which piece of • C++ has a mechanism for declaring individual code to execute when a method is called methods as dynamically dispatched • Static dispatch means that the decision is made • If an overriding function exists, call it statically, i.e. at compile time • The decision is made at run-time • Decision made based on static (declared) type of • Sometimes called "late binding". receiver • The mechanism: In base class, label the function with virtual keyword • Overriding versions in subclasses don’t need the Point *myPointPointer = new ColorPoint( 3.14, 2.78, green ); virtual keyword myPointPointer->print( cout ); but please use the keyword anyway for better style // myPointPointer is a Point*, so call Point::print 07/22/01 07/22/01 R-5 R-6 CSE 143 R
Example Of Dynamic Dispatch Dynamically-Dispatched Calls Point *p = new ColorPoint( 3.13, 5.66, ochre ); class Point { //base class p->print( cout ); public: virtual void print( ostream& os ); • The compiler notices that Point::print is defined … }; as virtual class ColorPoint : public Point { //derived class • Instead of just calling Point::print , it inserts public: extra code to look at information attached to the virtual void print( ostream& os ); … object by new to decide what function to call }; • This is slightly slower than static dispatch //in a client • Almost always too minor a speed penalty to worry about Point *p = new ColorPoint( 3.13, 5.66, ochre ); p->print( cout ); // calls ColorPoint::print( ) 07/22/01 07/22/01 R-7 R-8 When Does This Happen? Example Application • Dynamic dispatch ONLY happens when BOTH of • An array of pointers to objects derived from the these two conditions are met: same base class: 1. The object is accessed through a pointer (or reference) mammal * zoo[20]; // An array of 20 pointers. 2. The method is virtual • All the objects pointed to are mammals, but some • In ALL other cases, you get static dispatch might be dogs, people, aardvarks, hedgehogs, • Two common useful cases etc. • Parameters : Objects passed by pointer or reference to • Each class might have its own methods for a function behavior like “scream” “fight” “laugh”, etc. • Arrays : an array of pointers to objects • If I write zoo[i]->laugh() I want to get the appropriate behavior for that type of animal Won’t happen unless laugh is virtual in mammal class 07/22/01 07/22/01 R-9 R-10 Contrast Virtual Destructors • mammal mlist[20]; • If a class contains a destructor and is used as a base class, then the destructor should be • all array elements are of the same type declared virtual • Everything in the list is treated as a mammal, period regardless of whether methods are virtual or not • Ensures that correct destructor is called when a pointer • mammal * vmlist[20]; to that class is deleted, even if there are no other virtual functions • Each critter behaves like "mammal" for the non-virtual Class XYZ { public: functions, and like its own particular kind of mammal for ... the virtual methods. virtual ~XYZ(); ... }; • Puzzle: constructors are never virtual!! (Why?) 07/22/01 07/22/01 R-11 R-12 CSE 143 R
Abstract vs Concrete Classes Abstract Class in C++ • Some classes are so abstract that instances of • "abstract" and "concrete" are not keywords in C++ them shouldn’t even exist • Abstract classes are recognized by being classes • What does it mean to have an instance of widget ? of with unimplementable methods pushbutton ? of Animal ? • "pure virtual functions" (next slide) • It may not make sense to attempt to fully • Such a class is only intended to be used as a implement all functions in such a class base class • What should pushbutton::clicked() do? • An abstract class is one that should not or can not be instantiated - it only defines an interface • declaration of public methods, partial implementation • A concrete class can have instances 07/22/01 07/22/01 R-13 R-14 Pure Virtual Functions Draw the Hierarchy class hedgehog : public • Syntax: append " = 0 " to base method declaration class animal {... mammal {... class pushbutton : public widget { virtual dance ( ) = 0; // no "dance" method public: virtual void clicked() = 0; ... }; dig ( ); }; • A “pure virtual” function is not implemented in the walk ( ); base class class mammal : public walk (int, int); animal {... • must implement in derived classes ...}; dance ( ); • Compiler guarantees that class with pure virtual walk ( ); functions cannot be instantiated class seaUrchin : public animal {... ...}; • If you call a pure virtual function, you’ll use the dance ( ); version from some derived class sting ( ); }; pushbutton *b = new quitbutton; b->clicked(); 07/22/01 07/22/01 R-15 R-16 What’s Legal / Which function is Example Hierarchy called? (continued) class freshman : public • animal annie; class person {... student {... • hedgehog * hp; virtual walk ( ) = 0; enroll ( ); • hp->walk (); virtual run ( ); virtual run ( ); • animal * ap = hp; ...}; ...}; • ap -> dance(); • ap->walk(); class student : public person {... • mammal * mp = hp; enroll ( ); • mp->walk(); virtual walk ( ); ...}; 07/22/01 07/22/01 R-17 R-18 CSE 143 R
What’s Legal / Which function is Draw hierarchy & call graph called? (continued) Start your drawing at class lir : public plug { • person paula; plug::dispatch() public: • student *stu = new freshman(); virtual void boof() • stu->enroll(); class plug { { biff(); } public: • student sara = *stu; } virtual void boof() • sara.run(); { bang(); } class vop : public plug { • person *pp = stu; virtual void bang() public: { nalg(); } • pp->run(); void dispatch() virtual void bang() • pp->walk(); { trog->boof(); } { whing(); } • freshman *fred = pp; protected: protected: plug *trog; • fred->enroll(); int log; }; } 07/22/01 07/22/01 R-19 R-20 CSE 143 R
Recommend
More recommend