Presentation is loading. Please wait.

Presentation is loading. Please wait.

TCP1201 OOPDS Lecture 4 1. Learning Objectives  To understand upcasting & downcasting  To understand static polymorphism and dynamic polymorphism 

Similar presentations


Presentation on theme: "TCP1201 OOPDS Lecture 4 1. Learning Objectives  To understand upcasting & downcasting  To understand static polymorphism and dynamic polymorphism "— Presentation transcript:

1 TCP1201 OOPDS Lecture 4 1

2 Learning Objectives  To understand upcasting & downcasting  To understand static polymorphism and dynamic polymorphism  To understand the problem of static polymorphism  To enable dynamic polymorphism via virtual function  To understand the benefit of dynamic polymorphism  To understand abstract class  To understand why virtual destructor is needed 2

3 Upcasting and Downcasting An object of subclass can be treated as an object/pointer/reference of its superclass, and vice versa. Upcasting: casting a subclass object as superclass. – Can be implicit. – Widely used in polymorphism. Downcasting: casting a superclass object as subclass. – Must be explicit. – Rarely used, and is error-proned.

4 Upcasting and Downcasting class Super { }; class Sub : public Super { }; int main() { Super a; // a Super object. Sub b; // a Sub object. Sub* pb = &b; // Sub pointer points to Sub object. Super* pa = pb; // Super pointer points to Sub object, // ok, implicit upcasting. pa = &b; // Ok, implicit upcasting. Super& ra = b; // Super reference references a Sub object, // ok, implicit upcasting. pa = &a; // Super pointer points to Super object. pb = pa; // Sub pointer points to Super object, // compile-error, implicit downcasting. pb = (Sub*) pa; // Ok, explicit downcasting. Sub& rb1 = a; // Compile-error, implicit downcasting. Sub& rb2 = (Sub&) a; // Ok, explicit downcasting. pa = dynamic_cast (&b); // Ok, explicit upcasting. pa = static_cast (&b); // Ok, explicit upcasting. }

5 Benefits of Upcasting class Human { // Superclass public: void eat() { cout << "Eating\n"; } }; class Lecturer : public Human { }; // Subclass class Student : public Human { }; // Subclass void callEat (Human & h) { // Superclass reference. h.eat(); } int main() { Human* h[3] = { new Human, new Lecturer, new Student}; for (int i = 0; i < 3; i++) { h[i]->eat(); callEat (*h[i]); delete h[i]; } } Upcasting allows us to use an array of superclass pointers to point to objects from subclasses. Another benefit is we can pass subclass object to a function parameter of type superclass pointer/reference.

6 What is Polymorphism? -Polymorphism is allowing objects of different types to respond differently to the same method call. -Polymorphism occurs when there is function overriding. -In lecture 3, superclass Human has a speak method and is overridden by subclass Student. Invoking the speak method from Human has different result than Invoking the speak method from Student. 6

7 Different objects invoke different version of methods with the same name ( speak() ) class Animal { public: void speak() { cout << "I'm an animal\n"; } }; class Bird : public Animal { public: void speak() { // overriding cout << "I'm a bird\n"; } }; int main() { Animal* a = new Animal; a->speak(); // call Animal::speak() Bird* b = new Bird; b->speak(); // call Bird::speak() delete a; delete b; } Output: I'm an animal I'm a bird How does C++ knows which method to call? 7 Through function call binding

8 class Animal { public: void speak() {... } }; class Bird : public Animal { public: void speak() {... } }; int main() { Animal* a = new Animal; a->speak(); Bird* b = new Bird; b->speak(); delete a; delete b; } Bird::speak() Definition Animal::speak() Definition Memory 0x7723 0x77b4 Function Call Binding Connecting a function call to a function body is called binding Function call binding is the process of determining what block of function code is executed when a function call is made. 8

9 There are 2 types of function call binding (polymorphism): Early/Static/Compile-time binding/polymorphism: Binding is performed during compile-time, and is decided by the compiler and linker based on the variable used to invoke the method. Late/Dynamic/Runtime binding/polymorphism: Binding occurs at runtime, based on the type of the object calling the method. Function Call Binding 9

10 Static Polymorphism It is the polymorphism that is implemented using static binding. The compiler determines beforehand what method definition will be executed for a particular method call, by looking at the type of the variable invoking the method. We have been using static polymorphism thus far without realizing it. 10

11 Animal + move():void Bird + move():void Consider the following Animal hierarchy, subclass Bird overrides superclass Animal's move() method. Static Polymorphism class Animal { public: void move() { cout << "Moving\n"; } }; class Bird : public Animal { public: void move() { cout << "Flying\n"; } }; 11

12 int main() { Animal a1; a1.move(); // cout "Moving". Animal *a2 = new Animal; // Pointer. a2->move(); // cout "Moving". Animal& a3 = a1; // Reference. a3.move(); // cout "Moving". Bird b1; b1.move(); // cout "Flying". Bird *b2 = new Bird; // Pointer. b2->move(); // cout "Flying". Bird& b3 = b1; b3.move(); // cout "Flying". } Animal::move() is called. No problem Call the move() method from an Animal object, an Animal pointer/reference to an Animal object, a Bird object, or a Bird pointer/reference to a Bird object, got overriding but no upcasting => no problem Static Polymorphism 12 Bird::move() is called. No problem

13 Call the move() method from a Bird object using an Animal pointer/reference, got overriding and got upcasting => big problem. int main() { Bird* b1 = new Bird; Animal* a1 = b1; // Upcasting via pointer. a1->move(); // cout "Moving". Bird b2; a1 = &b2; // Upcasting via pointer. a1->move(); // cout "Moving". Animal& a2 = b2; // Upcasting via reference. a2.move(); // cout "Moving". delete b1; } Animal::move() is still called but Bird::move() is more appropriate/precise/accurate in the context. Static Polymorphism: Problem 13

14 Consider the following inheritance hierarchy: 14 class Animal { public: void move() { cout << "Moving\n"; } }; class Bird : public Animal { public: void move() { cout << "Flying\n"; } }; class Fish : public Animal { public: void move() { cout << "Swimming\n"; } }; class Mammal : public Animal { public: void move() { cout << "Walking\n"; } }; Static Polymorphism: Problem

15 To make a function callMove() to support every class in the animal inheritance hierarchy, we have to write/overload the callMove() function for every class. 15 // 1 callMove() only. void callMove (Animal & a) {... a.move(); } int main() { Animal a; Bird b; Fish f; Mammal m; callMove (a); callMove (b); callMove (f); callMove (m); } Static Polymorphism: Problem 1 Output: Moving // 4 callMove() for // 4 different classes. void callMove (Animal & a) {... a.move(); } void callMove (Bird & b) {... b.move(); } void callMove (Fish & f) {... f.move(); } void callMove (Mammal & m) {... m.move(); } Output: Moving Flying Swimming Walking

16 We can actually solve the problem in previous slides by using template. However the downside is the template function won't be limited to the Animal hierarchy anymore. 16 // template callMove(). // Work but not limited to Animal hierarchy. template void callMove (T & a) {... a.move(); } Static Polymorphism: Problem 1

17 We cannot use one for loop to call the correct version of move() method in an array/vector of objects from Animal hierarchy. 17 int main() { // Array of different animals. Animal* a[4] = {new Animal, new Bird, new Fish, new Mammal}; for (int i = 0 ; i < 4; i++) a[i]->move(); for (int i = 0 ; i < 4; i++) delete a[i]; } Current output: Moving Static Polymorphism: Problem 2 Preferred output: Moving Flying Swimming Walking

18 The solution to the upcasting problem we just discussed is to enable dynamic polymorphism or dynamic binding via the use of virtual function. Requirements for C++ dynamic polymorphism: There must be an inheritance hierarchy. The superclass must have a virtual method. Subclass must override superclass virtual method. A superclass pointer/reference to a subclass object (upcasting) is used to invoke the virtual method. Dynamic Polymorphism 18

19 Virtual Function class Animal { public: virtual void move() { // Enable dynamic binding. cout << "Moving\n"; } }; class Bird : public Animal { public: void move() { // Automatically virtual. cout << "Flying\n"; } }; In order to allow a method to be bound at run-time, it must be declared as virtual in the superclass. By declaring a method as virtual in superclass, it is automatically virtual in all subclasses. 19

20 Virtual Function We can use the keyword virtual at subclass' virtual function to remind ourselves that it is using dynamic binding. 20 class Animal { public: virtual void move() { cout << "Moving\n"; } }; class Bird : public Animal { public: virtual void move() { // Optional, as reminder. cout << "Flying\n"; } };

21 After making the move() method virtual: int main() { Bird* b1 = new Bird; Animal* a1 = b1; // Upcasting via pointer. a1->move(); // cout "Flying". Bird b2; a1 = &b2; // Upcasting via pointer. a1->move(); // cout "Flying". Animal& a2 = b2; // Upcasting via reference. a2.move(); // cout "Flying". delete b1; } Bird::move() is called now, which is more appropriate/precise/ accurate in the context. Dynamic Polymorphism 21

22 The primary benefit of dynamic polymorphism is it enables to write codes that work for all objects from the same inheritance hierarchy via upcasted pointer / reference. Consider the following inheritance hierarchy: Dynamic Polymorphism: Benefit 22 class Animal { public: virtual void move() { cout << "Moving\n"; } }; class Bird : public Animal { public: virtual void move() { cout << "Flying\n"; } }; class Fish : public Animal { public: virtual void move() { cout << "Swimming\n"; } }; class Mammal : public Animal { public: virtual void move() { cout << "Walking\n"; } };

23 We can write a single callMove() function that call the correct version of move() method for all objects in the Animal hierarchy. Dynamic Polymorphism: Benefit 23 void callMove (Animal & a) {... a.move(); } int main() { Animal a; Bird b; Fish f; Mammal m; callMove (a); callMove (b); callMove (f); callMove (m); } Output: Moving Flying Swimming Walking

24 We can also write a loop to call the correct version of move() method for all objects in the Animal hierarchy. Dynamic Polymorphism: Benefit 24 int main() { // Array of different animals. Animal* a[4] = {new Animal, new Bird, new Fish, new Mammal}; for (int i = 0 ; i < 4; i++) a[i]->move(); for (int i = 0 ; i < 4; i++) delete a[i]; } Output: Moving Flying Swimming Walking

25 Abstract Class An abstract class is a class that cannot be instantiated. A class that can be instantiated is called a concrete class. – All classes that we have learned so far are concrete classes. The reason to make a class abstract is, due to some requirements/constraints, we want to make it impossible to create instance(s) of the class. To be an abstract class: a class must have at least a pure virtual function. 25

26 Pure Virtual Function A pure virtual function is a method that is initialized to zero (0) in its declaration and no implementation is given in the class. Pure virtual function makes the class abstract and no instance of the class can be created. 26 class Animal { // Abstract class. public: virtual void move() = 0; // Pure virtual function. }; int main() { Animal a1; // ERROR: Animal is an abstract class. Animal* a2 = new Animal; // ERROR: same reason.... }

27 Pure Virtual Function Subclass of an abstract class must override all of the superclass pure virtual functions in order to become a concrete class (instantiate-able). 27 class Animal { // Abstract superclass. public: virtual void move() = 0; // Pure virtual function. }; class Bird : public Animal { // Concrete subclass. public: virtual void move() { // Overriding pure virtual function. cout << "Flying\n"; } }; int main() { Animal *a = new Bird; // Fine. create a Bird object. a->move(); // cout "Flying“. }

28 Pure Virtual Function 28 class Animal { // Abstract class. public: virtual greet() {} virtual void move() = 0; // Pure virtual function. }; class Bird : public Animal { // Abstract class. public: virtual void greet() {... } }; class Eagle : public Bird { // Concrete class. public: virtual void greet() {... } virtual void move() {... } // Overriding. }; Subclass that does not override superclass' pure virtual function is an abstract class too.

29 Virtual Destructor Superclass destructor should always be virtual in order for the delete operator to call the destructors correctly for an subclass object that is created via upcasting. Otherwise, the delete operator will call only superclass destructor, not the subclass destructor. – This will cause only the base part of the object to be destroyed. // Problem class Super { public: ~Super() { // Non virtual. cout << "Super destroyed\n"; } }; class Sub : public Super { public: ~Sub() { cout << "Sub destroyed\n"; } }; int main() { Super* p = new Sub; // Upcasting. delete p; // Invoke destructor. } Output: Super destroyed 29 // Where is Sub's destructor?

30 Virtual Destructor // Solution class Super { public: virtual ~Super() { cout << "Super destroyed\n"; } }; class Sub : public Super { public: virtual ~Sub() { cout << "Sub destroyed\n"; } }; int main() { Super* p = new Sub; // Upcasting. delete p; // Invoke destructor. } To ensure that subclass objects are destroyed properly, make superclass destructor virtual. Output: Sub destroyed Super destroyed 30

31 Good Programming Practices Use inheritance to promote code reusability. Use virtual function to describe common behavior within a family of classes. Use pure virtual function to force subclasses to define the virtual function. Use a pointer/reference to an abstract base class to invoke virtual function, thus implementing dynamic polymorphism. 31

32 Learning Objectives  To understand upcasting & downcasting  To understand static polymorphism and dynamic polymorphism  To understand the problem of static polymorphism  To enable dynamic polymorphism via virtual function  To understand the benefit of dynamic polymorphism  To understand abstract class  To understand why virtual destructor is needed 32

33 33 Static Photo Credit: j2sejava.wordpress.com

34 34 Dynamic Photo Credit: j2sejava.wordpress.com


Download ppt "TCP1201 OOPDS Lecture 4 1. Learning Objectives  To understand upcasting & downcasting  To understand static polymorphism and dynamic polymorphism "

Similar presentations


Ads by Google