1 Lecture 7 Polymorphism Virtual Functions Virtual Destructors

  • Slides: 54
Download presentation
1 Lecture #7 • • Polymorphism Virtual Functions Virtual Destructors Pure Virtual Functions

1 Lecture #7 • • Polymorphism Virtual Functions Virtual Destructors Pure Virtual Functions

2 Polymorphism Consider a function that accepts a Person as an argument Can we

2 Polymorphism Consider a function that accepts a Person as an argument Can we also pass a Student as a parameter to it? We know we can do this: void Lemonade. Stand(Person &p) { cout << “Hello “ << p. get. Name(); cout << “How many cups of ”; cout << “lemonade do you want? ”; } But can we do this? void main(void) { Person p; void main(void) { Student s; Lemonade. Stand(p); } Lemonade. Stand(s); }

3 Polymorphism Consider a function that accepts a Person as an argument Can we

3 Polymorphism Consider a function that accepts a Person as an argument Can we also pass a Student as a parameter to it? void Lemonade. Stand(Person &p) { cout << “Hello “ << p. get. Name(); cout << “How many cups of ”; Yes. I’m a person. I have cout << “lemonade do you want? ”; I’d like to buy a name and everything. } some. Ok. lemonade. How many cups of lemonade We only serve Mom. I think would you like? that’s Lucy. Are Liu! people. you a person? class Person { public: string get. Name(void); . . . private: string m_s. Name; int m_n. Age; }; Person Shhhh.

4 Polymorphism Consider a function that accepts a Person as an argument void Lemonade.

4 Polymorphism Consider a function that accepts a Person as an argument void Lemonade. Stand(Person &p) { cout << “Hello “ << p. get. Name(); cout << “How many cups of ”; cout << “lemonade do you want? ”; Well, Student asyou} can see by Can we also pass a I’mdeclaration based on a Well, as long as my class a parameter to it? Since We only serve I’d. I’m like to buy Hmm. a student Mom. It’s a But do you have Person, I have everyyou’re a person, we that allas students are people. Are NERD!!! but far as I some lemonade. Prove it like to us! a name a thing amore Person has… can serve you. just a specific youperson? a person? Uh huh. know, all students class Student : Including a name! Look! sub-class of people. are people! public Person { class Person public: { // new stuff: public: int get. Student. ID(); get. Name string get. Name(void); private: . . . // new stuff: int m_n. Student. ID; private: }; string m_s. Name; int m_n. Age; Student

5 Polymorphism The idea behind polymorphism is that once I define a function that

5 Polymorphism The idea behind polymorphism is that once I define a function that accepts a (reference or pointer to a) Person… Not only can I pass Person variables to that class… But I can also pass any variable that was derived from a Person! class Person { public: string get. Name(void) { return m_name; } class Student : public Person . . . { public: private: // new stuff: string m_name; int get. GPA(); int m_age; }; private: // new stuff: float m_gpa; }; void Say. Hi(Person &p) { cout << “Hello “ << p. get. Name(); } main() { float = 1. 6; Person. GPA p(“Eric”, 18); Student s(“David”, 19, GPA); } Say. Hi(p); Say. Hi(s);

Polymorphism Our Say. Hi function now treats 6 variable as if it referred toaa

Polymorphism Our Say. Hi function now treats 6 variable as if it referred toaa Why is this? Well a Student IS ap. Person. Everything Person can do, it Person can do. variable… In fact, Say. Hi no idea. Ithat So if I can ask for a Person’s name withhas get. Name, can p to a Student! ask for a Student’s namerefers with get. Name too! s Person’s Stuff class Person { string get. Name() { return m_name; } public: int get. Age() string get. Name(void) { return m_age; } { return m_name; } class Student : public Person . . . {m_name “David” m_age 52 public: private: Student’s Stuff // new stuff: string m_name; int get. GPA(); int m_age; float get. GPA() }; private: { return m_gpa; } // new stuff: m_gpa 1. 6 float m_gpa; }; void Say. Hi(Person &p) { cout << “Hello “ << p. get. Name(); } main() { float = 1. 6; Person. GPA p(“Eric”, 18); Student s(“David”, 52, GPA); } Say. Hi(p); Say. Hi(s);

7 Polymorphism Any time we use a base pointer or a base reference to

7 Polymorphism Any time we use a base pointer or a base reference to access a derived object, this is called polymorphism. class Person { public: string get. Name(void); . . . class Student : public Person private: { string m_s. Name; public: int m_n. Age; // new stuff: }; int get. Student. ID(); private: // new stuff: int m_n. Student. ID; }; void Say. Hi(Person *p) void Say. Hi(Person &p) { cout << “Hello “ << p->get. Name(); p. get. Name(); } main() { Student Person c; s(“Carey”, 38, 3. 9); Say. Hi(&s); Say. Hi(c); }

8 Polymorphism and Chopping! And that can You MUST use a pointer or reference

8 Polymorphism and Chopping! And that can You MUST use a pointer or reference for Now the Say. Hi function polymorphism to work! isn’t dealing with the original Student variable! Otherwise something called “chopping” Polymorphism only works when you use a happens… and that’s a bad thing! It has a chopped pointer to pass an object! cause all sorts reference or a of troubles! temporary variable that has no Student parts! class Person sp{ So right now, variable s would be Person’s Stuff public: “chopped”. string get. Name() string get. Name(void); { return m_name; } . . . C++ will class Student : basically chop off all the int get. Age() data/methods of the (Student) public Person private: { return m_age; } derived class and only send the base (Person) { string m_s. Name; “Carey” m_age 38 function! m_name parts of public: variable s to the int m_n. Age; // new stuff: }; int get. Student. ID(); Student’s Stuff private: float get. GPA() // new stuff: { return m_gpa; } int m_n. Student. ID; m_gpa }; 3. 9 void Say. Hi(Person &p) { cout << “Hello “ << p. get. Name(); } main() { Student Person c; s(“Carey”, 38, 3. 9); Say. Hi(s); Say. Hi(c); }

9 Polymorphism class Shape { public: virtual double get. Area() { return (0); }

9 Polymorphism class Shape { public: virtual double get. Area() { return (0); } . . . private: . . . }; Since alllet’s shapes havehas an area, Similarly, Circle its Now consider two we define a member function own c’torclasses: and an updated derived Square called get. Area function. and Circle. For simplicity, we’ll omit other Square its own c’tor as memberhas functions/variables well an updated likeas get. X(), set. X(), get. Area get. Y(), function that overrides get. Perimeter(), etc. the one from Shape. class Square: public Shape {Let’s consider a new class public: called Shape. Square(int side){ m_side=side; } virtual double get. Area() { return (m_side*m_side); } We’ll use it to represent private: different geometric shapes. int m_side; }; class Circle: public Shape { public: Circle(int rad){ m_rad = rad; } virtual double get. Area() { return (3. 14*m_rad); } private: int m_rad; };

10 Polymorphism void Print. Price. Sq(Square &x) { Lets say we’re a company cout

10 Polymorphism void Print. Price. Sq(Square &x) { Lets say we’re a company cout << “Cost is: $“; that sells glass windows. cout << x. get. Area() * 3. 25; } And we want to write a void Print. Price. Cir(Circle &x) program to compute the { cost of each window. cout << “Cost is: $“; cout << x. get. Area() * 3. 25; } For example, assume that each window is $3. 25 per main() square foot. { Square s(5); Let’s look at Circle c(10); a program s m_side 5 that computes the cost Print. Price. Sq(s); c m_rad Print. Price. Cir(c); for both square and 10 } circular windows. class Shape { public: virtual double get. Area() { return (0); } . . . private: . . . class Square: public Shape }; { public: Square(int side){ m_side=side; } virtual double get. Area() { return (m_side*m_side); } private: int m_side; }; class Circle: public Shape { public: Circle(int rad){ m_rad = rad; } virtual double get. Area() { return (3. 14*m_rad); } private: int m_rad; };

11 Polymorphism void Print. Price. Sq(Square &x) { Lets say we’re a company cout

11 Polymorphism void Print. Price. Sq(Square &x) { Lets say we’re a company cout << “Cost is: $“; that sells glass windows. cout << x. get. Area() * 3. 25; void Print. Price(Shape &x) }{ And we want to write a cout << “Cost is: $“; void Print. Price. Cir(Circle &x) cout << x. get. Area() * 3. 25; program to compute the {} cost of each window. cout << “Cost is: $“; cout << x. get. Area() * 3. 25; } For example, assume that each window is $3. 25 per main() square foot. { Square s(5); Let’s look at Circle c(10); a program that computes the cost Print. Price. Sq(s); Print. Price. Cir(c); for two different windows. } class Shape { public: virtual double get. Area() { return (0); } class Square: public Shape . . . { class Circle: public Shape private: public: { . . . Square(int side){ m_side=side; } public: }; virtual double get. Area() Circle(int rad){ m_rad = rad; } { return (m_side*m_side); } virtual double get. Area() private: { return (3. 14*m_rad); } int m_side; private: }; int m_rad; }; Both Squares and Circles are It works, but it’s inefficient. Shapes… Why should we write two And we know that canthing? get functions to do theyou same the area of a Shape. . . So how about if we do this. . .

12 Polymorphism class Shape { public: virtual double get. Area() { return (0); }

12 Polymorphism class Shape { public: virtual double get. Area() { return (0); } . . . private: . . . }; class Square: public Shape { public: Hmm. The user is calling Square(int side){ m_side=side; } Hmm. The user is calling the virtual function virtual double get. Area() virtual function get. Area. { return (m_side*m_side); } get. Area. private: 5 * 5 = 25 int m_side; This time, x refers to a }; Since x refers to a Square class Circle: public Shape void Print. Price(Shape &x) { cout << “Cost is: $“; cout << x. get. Area()*3. 25; } main() { Square s(5); Circle c(10); Print. Price(s); Print. Price(c); s m_side c m_rad Circle variable, so I’ll call its { variable, I’ll call its version of this function. public: of this function. Circle(int rad){ m_rad = rad; } virtual double get. Area() { return (3. 14*m_rad); } private: 3. 14*10*10 = 314 int m_rad; }; 5 When you call a virtual func, C++ figures out which is the 10 correct function to call…

13 Polymorphism class Shape { public: virtual double get. Area() { return (0); }

13 Polymorphism class Shape { public: virtual double get. Area() { return (0); } . . . private: . . . }; class Square: public Shape { public: Hmm. The user is calling Square(int side){ m_side=side; } the virtual function virtual double get. Area() { return (m_side*m_side); } get. Area. private: int m_side; }; Since x refers to a Shape class Circle: public Shape void Print. Price(Shape &x) { cout << “Cost is: $“; cout << x. get. Area()*3. 25; } main() { Square s(5); Shape sh; Circle c(10); Print. Price(sh); Print. Price(s); } Print. Price(c); sh { variable, I’ll call its version public: of this function. Circle(int rad){ m_rad = rad; } virtual double get. Area() { return (3. 14*m_rad); } private: int m_rad; }; It works in this case too…

14 So What is Inheritance? What is Polymorphism? Inheritance: We publicly derive one or

14 So What is Inheritance? What is Polymorphism? Inheritance: We publicly derive one or more classes D 1…Dn (e. g. , Square, Circle, Triangle) from a common base class (e. g. , Shape). All of the derived classes, by definition, inherit a common set of functions from our base class: e. g. , get. Area(), get. Circumference() Each derived class may re-define any function originally defined in the base class; the derived class will then have its own specialized version of those function(s). Polymorphism: Now I may use a Base pointer/reference to access any variable that is of a type that is derived from our Base class: void print. Price(Shape *ptr) { cout << “At $10/square foot, your price is: “; cout << “$” << 10. 00 * ptr->get. Area(); } Circle c(10); // rad=10 Square s(20); // width=20 print. Price(&c); print. Price(&s); The same function call automatically causes different actions to occur, depending on what type of variable is currently being referred/pointed to.

15 Why use Polymorphism? With polymorphism, it’s possible to design and implement systems that

15 Why use Polymorphism? With polymorphism, it’s possible to design and implement systems that are more easily extensible. Today: We define Shape, Square, Circle and Print. Price(Shape &s). Tomorrow: We define Parallelogram and our Print. Price function automatically works with it too! Every time your program accesses an object through a base class reference or pointer, the referred-to object automatically behaves in an appropriate manner all without writing special code for every different type!

16 Polymorphism class Shape { public: virtual double get. Area() { return (0); }

16 Polymorphism class Shape { public: virtual double get. Area() { return (0); } . . . private: . . . }; void Print. Price(Shape &x) { cout << “Cost is: $“; cout << x. get. Area()*3. 25; } main() { Square s(5); Circle c(10); Print. Price(s); Print. Price(c); class Square: public Shape { public: Square(int side){ m_side=side; } virtual double get. Area() { return (m_side*m_side); } private: class Circle: public Shape int m_side; { }; public: Circle(int rad){ m_rad = rad; } virtual double get. Area() { return (3. 14*m_rad); } private: int m_rad; }; When you use the virtual keyword, C++ figures out what class is being referenced and calls the right function. So the call to get. Area()… Might go here… Or even here…

17 Polymorphism class Shape { public: virtual double get. Area() { return (0); }

17 Polymorphism class Shape { public: virtual double get. Area() { return (0); } . . . private: . . . }; void Print. Price(Shape &x) { cout << “Cost is: $“; cout << x. get. Area()*3. 25; x. set. Radius(10); //// ERROR! } x. set. Side(10); } main() { Square s(5); Print. Price(s); Circle c(10); Print. Price(c); class Square: public Shape c sclass Circle: public Shape { { public: . . . virtual double get. Area() { return (m_side*m_side); } { return (3. 14*m_rad); } void set. Side(int side) void set. Radius(int new. Rad) { m_side = side; } { m_rad = new. Rad; } private: int m_side; int m_rad; }; }; 10 5 As we can see, our Print. Price method THINKS that every variable you pass in to it is JUST a Shape. It thinks it’s operating on a Shape it has no idea that it’s really operating on a Circle or a Square! This means that it only knows about functions found in the Shape class! Functions specific to Circles or Squares are TOTALLY invisible to it!

18 Polymorphism class Shape { public: virtual double get. Area() { return (0); }

18 Polymorphism class Shape { public: virtual double get. Area() { return (0); } . . . private: . . . }; class Square: public Shape { public: Square(int side){ m_side=side; } virtual double get. Area() Hmm. The user is calling { return (m_side*m_side); } private: the function get. Area. int m_side; }; class Circle: public Shape void Print. Price(Shape &x) { cout << “Cost is: $“; cout << x. get. Area()*3. 25; } main() { Square s(5); Circle c(10); Print. Price(s); Print. Price(c); { Since x is a Shape variable, public: I’ll call Shape’s get. Area Circle(int rad){ m_rad = rad; } function. double get. Area() virtual double get. Area() { return (3. 14*m_rad); } private: int m_rad; }; s m_side 5 c m_rad 10 Lets see what happens if we forget to use the virtual keyword.

19 Polymorphism When should you use the virtual keyword? 1. Use the virtual keyword

19 Polymorphism When should you use the virtual keyword? 1. Use the virtual keyword in both your base and derived classes any time you redefine a function in a derived class. 2. Always use the virtual keyword for destructors in your base and derived classes. 3. You can’t have a virtual constructor, so don’t try!

20 Polymorphism and Pointers class Shape { public: virtual double get. Area() { return

20 Polymorphism and Pointers class Shape { public: virtual double get. Area() { return (0); } . . . private: . . . }; main() { Square s(5); Square *p; p = &s; cout << p->get. Area(); } class Square: public Shape { public: Square(int side){ m_side=side; } virtual double get. Area() { return (m_side*m_side); } private: int m_side; }; Polymorphism works with pointers too. Let’s see! Clearly, we can use a Square pointer to access a Square variable. . .

21 Polymorphism and Pointers class Shape { public: virtual double get. Area() { return

21 Polymorphism and Pointers class Shape { public: virtual double get. Area() { return (0); } . . . private: . . . }; main() { Square s(5); Shape *p; p = &s; // OK? ? . . . } class Square: public Shape { public: Square(int side){ m_side=side; } virtual double get. Area() { return (m_side*m_side); } private: int m_side; }; Question: Can we point a Shape pointer to a Square variable? In general, may point ais Answer: Yes, you since a Square superclass pointer at aa a Shape, we may point to subclassed variable. Square using a Shape pointer.

Polymorphism and Pointers! 22 In this example, we’ll use a Shape pointer to point

Polymorphism and Pointers! 22 In this example, we’ll use a Shape pointer to point to either a Circle or a Square, then its area! Aha! The get shapeptr variable points to a Square. I’ll call class Square: public Shape main() {Square’s get. Area function. { public: Square s(5); Square(int side){ m_side=side; } Circle c(10); virtual double get. Area() Shape * shapeptr; { return (m_side*m_side); } choice ‘s’ char choice; private: 5*5 int m_side; shapeptr cout << “(s)quare or a (c)ircle: ”; }; Hmm. get. Area is a virtual cin >> choice; if (choice == ‘s’) function. What type of shapeptr = &s; s Square data: variable does shapeptr// upcast else shapeptr = &c; // upcast m_side: 5 point to? cout << “The area of your shape is: “; c Circle data: cout << shapeptr->get. Area(); m_rad: 10 } (s)quare or a (c)ircle: s The area of your shape is:

23 Polymorphism Circle main() { Circle c(1); Square s(2); Triangle t(4, 5, 6); Shape

23 Polymorphism Circle main() { Circle c(1); Square s(2); Triangle t(4, 5, 6); Shape *arr[100]; arr[0] = &c; arr[1] = &s; arr[2] = &t; Here’s another example where polymorphism is. Square useful. // redraw all shapes for (int i=0; i<3; i++) { arr[i]->plot. Shape(); } } We could add a virtual plot. Shape() method to our shape, circle, square and triangle classes. Triangle What if we were building a … design program graphics and wanted to easily draw each shape on the screen. Now our program simply asks each object to draw itself and it does!

24 Polymorphism and Pointers class Shape { public: virtual double get. Area() { return

24 Polymorphism and Pointers class Shape { public: virtual double get. Area() { return (0); } . . . private: . . . }; main() { Square *ps; Shape sh; ps = &sh; // OK? ? . . . } class Square: public Shape { public: Square(int side){ m_side=side; } virtual double get. Area() { return (m_side*m_side); } private: int m_side; }; Question: Can we point a Square pointer to a Shape variable? No: While all Squares are Shapes, all Shapes are not necessarily Squares. Or, said another way, you may never point a derived class pointer/ reference to a base class variable.

25 Virtual HELL! class Geek C++: “Hmmm. . I’m really a { High. Pitched.

25 Virtual HELL! class Geek C++: “Hmmm. . I’m really a { High. Pitched. Geek…” public: void tickle. Me() does it C++: What “And laugh() is aprint? virtual { method…” This line is using polymorphism! laugh(); } main() We’re using a base (Geek) pointer virtual void laugh() { to access a { cout << “ha ha!”; } Geek *ptr = new Derived (High. Pitched. Geek) object! }; High. Pitch. Geek; class High. Pitch. Geek: public Geek ptr->tickle. Me(); // ? { public: delete ptr; C++: “So I’ll call the proper, virtual void laugh() }High. Pitch. Geek version of { cout << “tee hee”; } laugh()!” }; class Baritone. Geek: public Geek { public: virtual void laugh() { cout << “ho ho ho”; } }; C++ always calls the ptr most-derived version of a function associated with a variable, as long High. Pitched. Geek as it’s marked variable virtual!

26 Polymorphism and Virtual Destructors You should always make sure that you use virtual

26 Polymorphism and Virtual Destructors You should always make sure that you use virtual destructors when you use inheritance/polymorphism. Next, we’ll look at an example that shows a program with and without virtual destructors. Consider the following class hierarchy…

27 Polymorphism and Virtual Destructors class Prof { public: Prof() { m_my. IQ =

27 Polymorphism and Virtual Destructors class Prof { public: Prof() { m_my. IQ = 95; } virtual ~Prof() { cout << “I died smart: ” cout << m_my. IQ; } private: int m_my. IQ; }; Summary: class Math. Prof: public Prof { public: Math. Prof(void) { m_p. Table = new int[6]; for (int i=0; i<6; i++) m_p. Table[i] = i*i; } virtual ~Math. Prof() { delete [] m_p. Table; } private: int *m_p. Table; }; All professors think they’re smart. (Hmm… is 95 smart? ? ? ) All math professors keep a set of flashcards with the first 6 square numbers in their head.

28 Virtual Destructors Hey Operating Sytem, I class Prof { public: Prof() { m_my.

28 Virtual Destructors Hey Operating Sytem, I class Prof { public: Prof() { m_my. IQ = 95; } class Math. Prof: public Prof need 24 bytes of memory. { public: Math. Prof(void) { m_p. Table = new int[6]; for (int i=0; i<6; i++) virtual ~Prof() m_p. Table[i] = i*i; Ok, we’re allocating a { } Math. Prof variable, virtual ~Math. Prof() I’ll cout << “I died smart: ” cout << m_my. IQ; { call the constructors… } delete [] m_p. Table; Hey Operating System, I private: } Prof then 8 need youfirst, to reserve int m_my. IQ; private: No worries! I’ll Math. Prof’s constructor }; int *m_p. Table; bytes of memory for me. Ok! I’ll reserve 24 main() reserve 8 bytes of }; second. { Prof *p; p = new Math. Prof; . . . delete p; } p 1000 bytes of memory for you at location 800. 0 location 1000. 1 Math. Prof data: m_p. Table: 800 Prof’s data: m_my. IQ: 95 4 9 16 25

29 Polymorphism and Virtual Destructors class Prof { public: Prof() { m_my. IQ =

29 Polymorphism and Virtual Destructors class Prof { public: Prof() { m_my. IQ = 95; } Ok. Now tell the class Math. Prof: public Prof { public: that I ran the destructors, I’ll Math. Prof(void) { Operating system to free the Hey OS, can you please m_p. Table = new int[6]; memoryfree for the me: 24 bytes at for (int i=0; i<6; i++) Hmm. Let’s see… address 800. virtual ~Prof() m_p. Table[i] = i*i; Hey OS, you { } can release the memory at cout << “I died smart: ” virtual ~Math. Prof() Even though p is a Prof pointer, it address 1000. cout << m_my. IQ; { actually points to a Math. Prof variable. } delete [] m_p. Table; Ok! I’ll free it for private: } int m_my. IQ; private: someone else to use. So I should call Math. Prof’s d’tor first }; int *m_p. Table; main() and then}; Prof’s d’tor second. { Prof *p; p = new Math. Prof; . . . delete p; } p 1000 Math. Prof data: m_p. Table: 800 Prof’s data: m_my. IQ: 95 0 1 4 9 16 25

Virtual Destructors 30 Now let’s see what happens if our destructors aren’t virtual functions*.

Virtual Destructors 30 Now let’s see what happens if our destructors aren’t virtual functions*. class Prof { public: Prof() { m_my. IQ = 95; } class Math. Prof: public Prof { public: Math. Prof(void) { m_p. Table = new int[6]; for (int i=0; i<6; i++) ~Prof() m_p. Table[i] = i*i; virtual ~Prof() { } cout << “I died smart: ” ~Math. Prof() virtual ~Math. Prof() cout << m_my. IQ; { } delete [] m_p. Table; Technically, if you don’t make your destructor private: } virtual your program will have undefined int m_my. IQ; private: }; behavior int *m_p. Table; }; * (e. g. , it could do anything, including crash), but what I’ll show you is the typical behavior.

31 Polymorphism and Virtual Destructors class Prof { public: Prof() { m_my. IQ =

31 Polymorphism and Virtual Destructors class Prof { public: Prof() { m_my. IQ = 95; } Hey Operating Sytem, I class Math. Prof: public Prof need 24 bytes of memory. { public: Math. Prof(void) { m_p. Table = new int[6]; for (int i=0; i<6; i++) ~Prof() m_p. Table[i] = i*i; Ok, we’re allocating a { } Math. Prof variable, ~Math. Prof() I’ll cout << “I died smart: ” cout << m_my. IQ; { call the constructors… } delete [] m_p. Table; private: Hey Operating System, } I Prof first, then int m_my. IQ; private: need you to reserve 8 No worries! I’ll Math. Prof’s constructor }; Ok! I’ll reserve 24 main() bytes of memory for int *m_p. Table; me. reserve 8 bytes of }; second. bytes of memory for { Prof *p; p = new Math. Prof; . . . delete p; } p 1000 memory for you at location 800. 0 location 1000. 1 Math. Prof data: m_p. Table: 800 Prof’s data: m_my. IQ: 95 4 9 16 25

32 Polymorphism and Virtual Destructors class Prof { public: Prof() { m_my. IQ =

32 Polymorphism and Virtual Destructors class Prof { public: Prof() { m_my. IQ = 95; } class Math. Prof: public Prof { public: Ok. Now that I ran the destructor, I’ll Math. Prof(void) Hmm. Let’s see…to free the tell the Operating system { m_p. Table = new int[6]; memory for me: The variable p is a Prof pointer. for (int i=0; i<6; i++) ~Prof() m_p. Table[i] = i*i; Hey OS, you can release the memory at { } So all I need to call is 1000. Prof’s destructor. cout << “I died smart: ” address ~Math. Prof() cout << m_my. IQ; { } delete [] m_p. Table; private: } Ok! I’ll free it for int m_my. IQ; private: someone else to use. }; int *m_p. Table; main() }; { Prof *p; p = new Math. Prof; . . . delete p; } p 1000 Math. Prof data: Utoh! This Math. Prof’s means we destructor have a 800 m_p. Table: was never memory called leak! and the table Prof’s data: m_my. IQ: 95 was never freed! 0 1 4 9 16 25

33 In this case, C++ will only call Virtual Destructors – What Happens? Person’s

33 In this case, C++ will only call Virtual Destructors – What Happens? Person’s destructor since p is a Person pointer and Person’s In fact, our code works So what happens if we’ve forgotten isn’t virtual! finedestructor in this case. to make ajust class’s virtual? class Person destructor { public: . . . ~Person() virtual ~Person( ) { cout << “I’m old!” } }; If you forget a virtual destructor, And then define a derived variable it only causes problems when you in our program? use polymorphism: Will both destructors be called? class Prof: public Person { public: . . . virtual ~Prof( ) ~Prof() { cout << “Argh! No tenure!” } }; int main(void) { Person *p = new Prof; Prof carey; . . . delete p; // problem! } } // carey’s destructed But to. No be tenure! safe, if you use Argh! inheritance ALWAYS use virtual I’m old! destructors – just in case.

34 How does it all work? class Shape When you define a variable of

34 How does it all work? class Shape When you define a variable of a class… C++ adds an (invisible) table to your object that points to the proper set of functions to use. s get. X get. Y get. Area m_x int main() { Shape s; } m_y { public: virtual int get. X() {return m_x; } virtual int get. Y() {return m_y; } virtual int get. Area() {return 0; }. . . }; class Square: public Shape { public: This table is called a “vtable. ” virtual int get. Area() { return (m_side*m_side); } It contains an entry for … every virtual function in }; our class Circle: public Shape In the case of a Shape { public: variable, all three virtual int get. Area() pointers in our vtable { return (3. 14*m_rad); } point to our Shape class’s … functions. };

35 How does it all work? class Shape q { public: get. X get.

35 How does it all work? class Shape q { public: get. X get. Y get. Area virtual int get. X() {return m_x; } virtual int get. Y() {return m_y; } virtual int get. Area() {return 0; }. . . }; m_y m_x m_side Ok, how about if we define a Square variable? int main() { Shape s; Square q; } s get. X get. Y get. Area m_x m_y class Square: public Shape { public: virtual int get. Area() { return (m_side*m_side); } … }; However, our Square class Circle: public Shape Well, our Square has basically uses our { its own get. Area() Shape’s get. X and public: function… virtual int get. Area() get. Y functions, so { return (3. 14*m_rad); } its vtable our So other entriesentry will … points to that point there. }; version…

36 C++ uses the vtable at run-time How does it all work? class Shape

36 C++ uses the vtable at run-time How does it all work? class Shape C++: “I see. In this q (not case, compile-time) to figure out { p points to a public: which virtual function to call. Square, and Squares get. X get. Y get. Area virtual int get. X() {return m_x; } m_y m_x The }; m_side Now, when And wethis callworks a when we use member polymorphism function… too! int main() { Shape s; s get. X get. Y get. Area m_x p Square q; } cout << s. get. Area(); Shape *p = &q; cout << p->get. Area(); have their own virtual int get. Y() {return m_y; } specialized of complex, virtual int get. Area() {return 0; } details are aversion bit more but. . . get. Area() this is the general idea. m_y class Square: public Shape { public: virtual int get. Area() C++: { return (m_side*m_side); } “Aha! p points to … some type of Shape… }; C++ knows exactly Let’s see which version class Circle: public Shape where to go!of { the get. Area function to It just looks at the public: use…” vtable for “s” and virtual int get. Area() { return (3. 14*m_rad); } uses the right … function! };

37 Summary of Polymorphism • First we figure out what we want to represent

37 Summary of Polymorphism • First we figure out what we want to represent (like a bunch of shapes) • Then we define a base class that contains functions common to all of the derived classes (e. g. get. Area, plot. Shape). • Then you write your derived classes, creating specialized versions of each common function: Square version of get. Area Circle version of get. Area virtual int get. Area(void) { return(m_side * m_side); } virtual int get. Area(void) { return(3. 14*m_rad); } • You can access derived variables with a base class pointer or reference. • Finally, you should (MUST) always define a virtual destructor in your base class, whether it needs it or not. (no vd in the base class, no points!)

Useless Functions 38 !!Remember!! You always class Shape Question: …and when When I call

Useless Functions 38 !!Remember!! You always class Shape Question: …and when When I call the need a virtual destructor So here’s my question: { Print. Info function and public: in your base class when When would Shape’s pass in in a a Square, Circle, what virtual double get. Area() { return(0); } using polymorphism! get. Area() and virtual double get. Circum(){ return(0); } get. Area and get. Circum . . . virtual ~Shape() { … } get. Circum() functions doesfunctions it it call? }; class Square: public Shape { public: virtual double get. Area() { return (m_side*m_side); } virtual double get. Circum() { return (4*m_side); } . . . class Circle: public Shape { public: virtual double get. Area() { return (3. 14*m_rad); } virtual double get. Circum() { return (2*3. 14*m_rad); } … ever be called? void Print. Info(Shape &x) { cout << “The area is “ << x. get. Area(); cout << “The circumference is ” x. get. Circum(); } main() { Square s(5); Circle c(10); Print. Info(s); Print. Info(c);

39 class Shape { public: virtual double get. Area() { return(0); } virtual double

39 class Shape { public: virtual double get. Area() { return(0); } virtual double get. Circum(){ return(0); } . . . But why }; class Square: public Shape { public: virtual double get. Area() { return (m_side*m_side); } virtual double get. Circum() { return (4*m_side); } . . . class Circle: public Shape { public: virtual double get. Area() { return (3. 14*m_rad); } virtual double get. Circum() { return (2*3. 14*m_rad); } … Useless Well, I guess they’d be called. Functions if you created a Shape variable in main… would we ever want to get the area and circumference of an “abstract” shape? void Print. Info(Shape &x) Those are just dummy functions… { They return zero! cout << “The area is “ << x. get. Area(); They were never meant to be cout << “The circumference is ” used… x. get. Circum(); } main() { Square s(5); Shape p; Circle c(10); Print. Info(p); Print. Info(s); Print. Info(c);

40 Pure Virtual Functions We must define functions that are common to all derived

40 Pure Virtual Functions We must define functions that are common to all derived classes in our base class or we can’t use polymorphism! class Shape { public: virtual float get. Area() { return (0); } virtual float get. Circum() { return (0); } . . . }; class Square: public Shape { public: virtual float get. Area() { return (m_side*m_side); } virtual float get. Circum() { return (4*m_side); } . . . void Print. Price(Shape &x) { cout << “Cost is: $“; But these functions in our cout << x. get. Area()*3. 25; base } class are never actually ? used – they just define main() common functions for the { Square s(5); derived classes. Print. Price(s); } class Circle: public Shape { public: virtual float get. Area() { return (3. 14*m_rad*m_rad); } virtual float get. Circum() { return (2*3. 14*m_rad); } …

41 Pure Virtual Functions So what we’ve done so far is to define a

41 Pure Virtual Functions So what we’ve done so far is to define a dummy version of these functions in our base class: class Shape { public: virtual float get. Area() = 0; { return (0); } { cout << “I am useless!n”; } virtual float get. Circum() = 0; { cout << “Me too!n”; } { return (0); } . . . private: }; C++ actually has an “officially correct” way to define such “abstract” functions. Let’s see how! These are called “pure virtual” functions. Since these funcs in our base class are never used, we could just as easily change their logic to … But it would be better if we could totally remove this useless logic from our base class! Rule: Make a base class function pure virtual if you realize: the base-class version of your function doesn’t (or can’t logically) do anything useful.

42 Pure Virtual Functions A pure virtual function is one that has no actual

42 Pure Virtual Functions A pure virtual function is one that has no actual { code }. If your base class has a pure virtual function… Your derived classes must define their own version of it. class Shape { public: virtual float get. Area() = 0; { return (0); } virtual float get. Circum() = 0; { return (0); } . . . private: }; class Circle: public Shape { class Square: public Shape { public: Circle(int rad){ m_rad = rad; } Square(int rad){ m_rad = rad; } virtual float get. Area() { return (3. 14*m_rad); } { return (m_side*m_side); } virtual float get. Circum() { return (2*3. 14*m_rad); } { return(4*m_side); } private: . . . };

43 Pure Virtual Functions If you define at least one pure virtual function in

43 Pure Virtual Functions If you define at least one pure virtual function in a base class, then the class is called an “abstract base class” class Shape { public: virtual double get. Area() = 0; virtual void some. Other. Func() { cout << “blahn”; . . . } . . . private: }; So, in the above example… get. Area is a pure virtual function, and Shape is an abstract base class.

44 Abstract Base Classes (ABCs) If you define an abstract base class, its derived

44 Abstract Base Classes (ABCs) If you define an abstract base class, its derived class(es): 1. Must either provide { code } for ALL pure virtual functions, 2. Or the derived class becomes an abstract base class itself! class Robot { public: virtual void talk. To. Me() = 0; virtual int get. Weight( ) = 0; . . . }; class Friendly. Robot: Killer. Robot: public Robot { public: virtual void talk. To. Me() { cout << “I must like geeks. ”; destroy} geeks. ”; }. . . virtual int get. Weight() { return 100; }. . . }; }; So is Robot a regular class or an ABC? Right! It’s an ABC Finally, how about Big. Happy. Robot? How about Friendly. Robot? Is it. How a regular class or an ABC? about Regular class. Killer. Robot? or an ABC? Regular class or an ABC? Right! It’s a regular class because It’s an ABC because it. Right! has no unimplemented pure Right! It’sprovide afunctions! regular class it doesn’t a body virtual because provides { code } foritget. Weight() for. It both of Robot’s PV functions! inherits Friendly. Robot’s version of talk. To. Me( and virtual defines Effectively it has a )pure itsversion own version of get. Weight( ) too! )!

45 Abstract Base Classes (ABCs) Why should you use Pure Virtual Functions and create

45 Abstract Base Classes (ABCs) Why should you use Pure Virtual Functions and create Abstract Base Classes anyway? class Shape { public: virtual float get. Area() = 0; { return (0); } virtual float get. Circum() = 0; Compiler: { return (0); } “That’s much better. . . . class Rectangle: public Shape private: Don’t screw up like { }; that again!” public: virtual float get. Area() { return (m_w * m_h); } 10 * 20 virtual float get. Circum() { return (2*m_w+2*m_h); }. . . }; user to Had. You we force made the get. Area( ) and implement )certain functions get. Circum( pure virtual, this to couldn’t prevent have common mistakes! happened! Ack– our rectangle Compiler: should have a For example, what if we create a “You have a syntax error of 60, Rectanglecircumference class that forgets to you silly programmer! not 0!!!get. Circum( This is a bug! define its own )? There is no get. Circum( ) function to call!” main() { Rectangle r(10, 20); cout << r. get. Area(); // OK cout << r. get. Circum(); //? }

46 What you can do with ABCs Even though you CAN’T create a variable

46 What you can do with ABCs Even though you CAN’T create a variable with an ABC type… main() { Shape s; !ERROR! cout << s. get. Area(); } So to summarize, use pure virtual functions to: (a) avoid writing “dummy” logic in a base class when it makes no sense to do so! (b) force the programmer to implement functions in a derived class to prevent bugs You can still use ABCs like regular base classes to implement polymorphism… void Print. Price(Shape &x) { cout << “Cost is: $“; cout << x. get. Area()*3. 25; } main() { Square s(5); Print. Price(s); Rectangle r(20, 30); Print. Price(r); }

47 Pure Virtual Functions/ABCs class Animal !!Remember!! You always Animal is an ABC, since

47 Pure Virtual Functions/ABCs class Animal !!Remember!! You always Animal is an ABC, since it { need a virtual destructor has two pure virtual public: in your base class when virtual void Get. Num. Legs(void) = 0; functions. using polymorphism! virtual void Get. Num. Eyes(void) = 0; . . . virtual ~Animal() { … } Insect is also an ABC, }; class Insect: public Animal { public: void Get. Num. Legs(void) { return(6); } // Insect does not define Get. Num. Eyes. . . }; class Fly: public Insect { public: void Get. Num. Eyes(void) { return(2); }. . . }; since it has at least one pure virtual function. Fly is a regular class, since it has no pure virtual functions. main() { Animal x; // OK? ? Insect y; // OK? ? Fly z; // OK? ? Animal *ptr = &z; // OK? ? }

48 Polymorphism Cheat Sheet You can’t access private members of the base class from

48 Polymorphism Cheat Sheet You can’t access private members of the base class from the derived class: // BAD! class Base { public: … private: int v; }; // GOOD! class Base { public: Base(int x) { v = x; } void set. V(int x) { v = x; } … private: int v; }; class Derived: public Base { public: Derived(int q) { v = q; // ERROR! } Derived(int q) : Base(q) // GOOD! { … } }; void foo() { v = 10; // ERROR! } }; void foo() { set. V(10); // GOOD! } Always make sure to add a virtual destructor to your base class: // BAD! class Base { public: ~Base() { … } // BAD! … }; // GOOD! class Base { public: virtual ~Base() { … } // GOOD! … }; class Derived: public Base { … }; class Person { public: virtual void talk(string &s) { … }; class Professor: public Person { public: void talk(std: : string &s) { cout << “I profess the following: “; Person: : talk(s); // uses Person’s talk } }; So long as you define your BASE version of a function with virtual, all derived versions of the function will automatically be virtual too (even without the virtual keyword)! Don’t forget to use virtual to define methods in your base class, if you expect to redefine them in your derived class(es) To call a baseclass method that has been redefined in a derived class, use the base: : prefix!

49 class Some. Base. Class { public: virtual void a. Virtual. Func() { cout

49 class Some. Base. Class { public: virtual void a. Virtual. Func() { cout << “I’m virtual”; } // #1 void not. Virtual. Func() { cout << “I’m not”; } // #2 void tricky() // #3 { a. Virtual. Func(); // *** not. Virtual. Func(); } }; class Some. Derived. Class: public Some. Base. Class { public: void a. Virtual. Func() { cout << “Also virtual!”; } void not. Virtua. Funcl() { cout << “Still not”; } }; // #4 // #5 main() { Some. Derived. Class d; Some. Base. Class *b = &d; // base ptr points to derived obj // Example #1 cout << b->a. Virtual. Func(); // calls function #4 // Example #2 cout << b->not. Virtual. Func(); // calls function #2 } // Example #3 b->tricky(); // calls func #3 which calls #4 then #2 Polymorphism Cheat Sheet, Page #2 Example #1: When you use a BASE pointer to access a DERIVED object, AND you call a VIRTUAL function defined in both the BASE and the DERIVED classes, your code will call the DERIVED version of the function. Example #2: When you use a BASE pointer to access a DERIVED object, AND you call a NON-VIRTUAL function defined in both the BASE and the DERIVED classes, your code will call the BASE version of the function. Example #3: When you use a BASE pointer to access a DERIVED object, all function calls to VIRTUAL functions (***) will be directed to the derived object’s version, even if the function (tricky) calling the virtual function is NOT VIRTUAL itself.

50 Challenge Problem: Diary Class Write a Diary class to hold your memories. .

50 Challenge Problem: Diary Class Write a Diary class to hold your memories. . . : 1. When a Diary object is constructed, the user must specify a title for the diary in the form of a C++ string. 2. All diaries allow the user to find out their title with a get. Title() method. 3. All diaries have a write. Entry() method. This method allows the user to add a new entry to the diary. All new entries should be directly appended onto the end of existing entries in the diary. 4. All diaries can be read with a read() method. This method takes no arguments and returns a string containing all the entries written in the diary so far. (You should expect your Diary class will be derived from!)

51 Diary Class Solution class Diary { public: Diary(const string &s) { m_s. Title

51 Diary Class Solution class Diary { public: Diary(const string &s) { m_s. Title = s; } virtual ~Diary() { /* do nothing*/ } // required!!! string get. Title(void) const { return(m_s. Title); } virtual void write. Entry(const string &s. Entry) { m_s. Entries += s. Entry; } virtual string read(void) const { return(m_s. Entries); } private: string m_s. Entries, m_s. Title; };

52 Challenge Problem Part 2 Now you are to write a derived class called

52 Challenge Problem Part 2 Now you are to write a derived class called “Secret. Diary”. This diary has all of its entries encoded. 1. Secret diaries always have a title of “TOP-SECRET”. 2. Secret diaries should support the get. Title() method, just like regular diaries. 3. The Secret. Diary has a write. Entry method that allows the user to write new encoded entries into the diary. - You can use a function called encode() to encode text 4. The Secret. Diary has a read() method. This method should return a properly decoded string containing all of the entries in the diary. - You can use a function called decode() to decode text

53 Class Secret. Diary: public Diary { Public: Secret. Diary() : Diary(“TOP-SECRET”) { }

53 Class Secret. Diary: public Diary { Public: Secret. Diary() : Diary(“TOP-SECRET”) { } virtual void write. Entry(const string &s) { Diary: : write. Entry(encoded(s)); } virtual string read(void) const { return decode(Diary: : read()); } Private: };

54 Challenge Problem Part 3 One of the brilliant CS students in CS 32

54 Challenge Problem Part 3 One of the brilliant CS students in CS 32 is having a problem with your classes (let’s assume you have a bug!). He says the following code properly prints the title of the diary, but for some reason when it prints out the diary’s entries, all it prints is gobbledygook. main() { Secret. Diary a; a. write. Entry(“Dear diary, ”); a. write. Entry(“Those CS 32 professors are sure great. ”); a. write. Entry(“Signed, Ahski Issar”); Diary *b = &a; cout << b->get. Title(); cout << b->read(); } What problem might your code have that would cause this?