1 / 40

Foundations: Language Mechanisms and Primitive OO Concepts Lecture 2: Polymorphism

Foundations: Language Mechanisms and Primitive OO Concepts Lecture 2: Polymorphism. Topics: Polymorphism and virtual functions in C++ Simple uses of polymorphism Implementation of virtual and non-virtual functions Common mistakes and need for virtual destructors. Pop quiz.

palma
Download Presentation

Foundations: Language Mechanisms and Primitive OO Concepts Lecture 2: Polymorphism

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Foundations: Language Mechanisms and Primitive OO ConceptsLecture 2: Polymorphism Topics: Polymorphism and virtual functions in C++ Simple uses of polymorphism Implementation of virtual and non-virtual functions Common mistakes and need for virtual destructors CSE 335: Software Design

  2. Pop quiz Question: Which method invoked to carry out this operation when empl is actually an instance of Manager? void generateReport( Employee& empl ) { ... empl.print(cout); ... } int main(void) { Employee doe(“John”, “Doe”, 235); Manager howell(“Charles”, “Howell”, 235, 3); generateReport(howell); } CSE 335: Software Design

  3. Motivation We should be able to develop clients that request an object to perform some operation and leave it to the object to choose the method This capability, called polymorphism, is one of the defining characteristics of OO In C++, we implement polymorphic operations by declaring member functions to be virtual CSE 335: Software Design

  4. Observe: function member declared virtual in base class. Example: declaring virtual function class Employee { private: string first_name, last_name; short department; public: virtual void print( ostream& ) const; }; class Manager : public Employee { private: list<Employee*> group; short level; public: void print( ostream& ) const; }; Observe:no need to “redeclare” print as virtual. CSE 335: Software Design

  5. Example: Virtual functions int main(void) { Employee doe(“John”, “Doe”, 235); Manager howell(“Charles”, “Howell”, 235, 3); doe.print(cout); // invokes Employee::print() howell.print(cout); // invokes Manager::print() Employee* ePtr = &howell; ePtr->print(cout); // invokes Manager::print() } CSE 335: Software Design

  6. Virtual-function definitions void Employee::print( ostream& os ) const { os << “Name:” << first_name << ““ << last_name << endl << “Dept: “ << department; } void Manager::print( ostream& os ) const { Employee::print(os); os << “Level: “ << level; } Question:What happens here??? CSE 335: Software Design

  7. Exercise We want to store different geometric objects in a list and compute their area. Develop a class hierarchy with classes Shape, Circle, and Rectangle. The following code should compile correctly: list<Shape*> myShapes; Circle* circ = new Circle(10); Rectangle* rect = new Rectangle(20, 40); myShapes.push_back(circ); myShapes.push_back(rect); for (list<Shape*>::iterator it = myShapes.begin(); it != myShapes.end(); it++) cout << (*it)->area() << endl; CSE 335: Software Design

  8. Heterogeneous containers Inheritance + polymorphism can be used to implement containers that can hold different types of elements • Declare base class from which all element classes derive • Base class declares a polymorphic operation • In C++, developer of base class declares a virtual member function • Each element class may provide its own method for the operation • Instantiate container with type “pointer to base class” • Clients, invoke operations on elements taken from container without knowing actual type of the elements! CSE 335: Software Design

  9. <<DETOUR>> Henceforth, we will use polymorphism frequently and in combination with other language features to build reliable and reusable software structures Because it is so central, it is important that you REALLY understand how it works My theory: No better way than to look at how it is implemented! CSE 335: Software Design

  10. Question Thinking in terms of system-level resources, what is the mechanism by which virtual functions are actually implemented? Answer: Each instance of a class that declares or inherits virtual member functions carries with it a pointer to a table (called a vtable) of pointers to member functions • When a virtual member function is invoked, a pointer to the function to be dispatched is found by looking it up in the vtable. • Instances of different classes point to different vtables. • Owing to this indirection, a client may invoke the method appropriate to the class used to originally instantiate the object without knowing that class! CSE 335: Software Design

  11. Process partitioned into 2 major chunks: static part contains program code + ... static data, vtables, etc dynamic part contains run-time stack + heap all local variables and objects created using new live here Recall: Conceptual model of memory use by a running process Run-time stack Heap Program code + static data CSE 335: Software Design

  12. Consider the following source code class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … void vf1(…){…} void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } Note: No virtual member functions yet; we’ll bring those in later. CSE 335: Software Design

  13. Code compiled and linked... class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … void vf1(…){…} void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } a.out Compilation/linking C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} CSE 335: Software Design

  14. Program is executed... class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … void vf1(…){…} void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } Executing process C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} CSE 335: Software Design

  15. Program is executed... class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … void vf1(…){…} void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } Process begins executing in function main Executing process C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} CSE 335: Software Design

  16. Object myC1 constructed... attr1 myC1 8 class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … void vf1(…){…} void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } attr2 32.0 C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} CSE 335: Software Design

  17. Object myC2 constructed attr1 8 myC1 class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … void vf1(…){…} void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } attr2 32.0 attr1 16 myC2 attr2 -64.5 C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} CSE 335: Software Design

  18. vf2() invoked on myC2 attr1 8 myC1 class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … void vf1(…){…} void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } attr2 32.0 attr1 16 myC2 attr2 -64.5 this C::vf2 activation … C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} CSE 335: Software Design

  19. Now let’s bring in the virtuals! CSE 335: Software Design

  20. Consider these modifications to our source code class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } Note: Only change to source is introduction of the 2 virtual keywords CSE 335: Software Design

  21. Compiling and linking… class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } a.out Class C vtable C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} Note: Existence of virtual functions in class C caused the compiler to create a static object called the class C vtable. CSE 335: Software Design

  22. Program is executed class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } Class C vtable C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} CSE 335: Software Design

  23. Object myC1 created... vptr myC1 class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } attr1 8 attr2 32.0 Class C vtable C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} CSE 335: Software Design

  24. Object myC1 created... vptr myC1 class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } attr1 8 attr2 32.0 Note: “extra” field in myC1 Class C vtable C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} CSE 335: Software Design

  25. Creating myC2 vptr class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } myC1 attr1 8 attr2 32.0 vptr myC2 attr1 16 attr2 -64.5 Class C vtable C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} CSE 335: Software Design

  26. Creating myC2 vptr class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } myC1 attr1 8 attr2 32.0 vptr myC2 attr1 16 attr2 Note: “extra” field in myC2 -64.5 Class C vtable C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} CSE 335: Software Design

  27. Dynamic dispatch of vf2 [step 1] vptr class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } myC1 attr1 8 attr2 32.0 vptr myC2 attr1 16 attr2 -64.5 Class C vtable C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} CSE 335: Software Design

  28. Dynamic dispatch of vf2 [step 2] vptr class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } myC1 attr1 8 attr2 32.0 vptr myC2 attr1 16 attr2 -64.5 Class C vtable C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} CSE 335: Software Design

  29. Dynamic dispatch of vf2 [step 3] vptr class C { protected: unsigned attr1; float attr2; … public: void f1(…){…} … virtual void vf1(…){…} virtual void vf2(…){…} }; int main(void) { C myC1(8,32.0); C myC2(16, -64.5); myC2.vf2(); } myC1 attr1 8 attr2 32.0 vptr myC2 attr1 16 attr2 -64.5 C::vf2 activation ... Class C vtable C::f1(…){…} … C::vf1(…){…} … C::vf2(…){…} main() {…} CSE 335: Software Design

  30. More interesting example class D : public C { protected: unsigned attr3; … public: void f1(…); … void vf1(…); void vf2(…); virtual void vf3(…); }; int main(void) { C myC1(8,32.0); D myD1(16, -64.5, 38); myD1.vf2() } CSE 335: Software Design

  31. More interesting example class D : public C { protected: unsigned attr3; … public: void f1(…); … void vf1(…); void vf2(…); virtual void vf3(…); }; int main(void) { C myC1(8,32.0); D myD1(16, -64.5, 38); myD1.vf2() } Question: How would the process memory map look in this example? CSE 335: Software Design

  32. Class C vtable More interesting example class D : public C { protected: unsigned attr3; … public: void f1(…); … void vf1(…); void vf2(…); virtual void vf3(…); }; int main(void) { C myC1(8,32.0); D myD1(16, -64.5, 38); myD1.vf2(); } myC1 main(…) vptr C::f1(…) attr1 8 D::vf2(…) attr2 32.0 C::vf1(…) D::vf3(…) C::vf2(…) D::vf1(…) myD1 D::f1(…) vptr attr1 16 attr2 -64.5 attr3 38 Class D vtable CSE 335: Software Design

  33. Exercise Observe:Actual parameter is a value, not a reference. void myPrint( Employee e ) { e.print(cout); } int main(void) { Manager howell( … ); myPrint(howell); } Question:Which print method invoked when actual parameter is Manager? CSE 335: Software Design

  34. Question A compiler translates a program in one language into an equivalent program in another language, which is “closer to the machine”. How would the compiler-generated code for function myPrint differ when Employee::print is virtual vs. non-virtual? CSE 335: Software Design

  35. Code for myPrint (print NOT virtual) pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax pushl $_cout pushl %eax call _print__8EmployeeR7ostream movl %ebp, %esp popl %ebp ret CSE 335: Software Design

  36. Code for myPrint (print virtual) pushl %ebp movl %esp, %ebp movl 8(%ebp), %edx pushl $_cout pushl %edx movl 4(%edx), %ecx ; put vptr in ecx movl 12(%ecx), %eax ; put address at offset ; 12 in vtable into eax call *%eax ; call function pointed to ; by eax movl %ebp, %esp popl %ebp ret CSE 335: Software Design

  37. Observations Lots of things happen when we define an member function to be virtual • Better left to the compiler • No significant inefficiency (one extra indirection per virtual function invocation) CSE 335: Software Design

  38. Destructors in derived classes class Target { public: Target() { numtargets++; } ~Target() { --numtargets; } static int numberOfTargets() { return numtargets; } private: static int numtargets; }; class Tank : public Target { public: Tank( const string& tId ) : tankId(tId) { numtanks++; } ~Tank() { numtanks--; } static int numberOfTanks() { return numtanks; } private: static int numtanks; const string tankID; }; CSE 335: Software Design

  39. Exercise Notice: base and derived classes Target* target = new Tank(“T-19”); . . . delete target; What happens here? (Think about what destructors are called) CSE 335: Software Design

  40. Virtual Destructor class Target { public: Target() { numtargets++; } virtual ~Target () { --numtargets; } static int numberOfTargets() { return numtargets; } private: static int numtargets; }; Solution: Make destructor virtual. CSE 335: Software Design

More Related