You are on page 1of 20

Lecture 11

Computer Programming II

1

Overview
 Introduction
 Static Polymorphism: Problem
 Virtual Function
 Dynamic Polymorphism: Benefit
 Virtual Destructor
 Abstract Base Class
 Pure Virtual Function
 Good Programming Practices
2

 Early/Static/Compile-time binding is the binding that is performed before the program is run or during compilation.Introduction  Recall that binding is a process of connecting a function call to a function body.  A method is dynamic-polymorphic if its binding occurs at run time rather than compile time.  Late/Dynamic/Runtime binding is the binding that is performed at run-time. 3 .

} }. } }. Animal + move() Bird + move() 4 .Static Polymorphism: Problem Recall the Animal hierarchy. class Bird : public Animal { public: void move() { cout << "Flying". if subclass Bird does override superclass Animal move() method class Animal { public: void move() { cout << "Moving".

Animal* a = &b.. Animal::move() } is still called but Bird::move() is more appropriate/precise/accurate in the context. Animal* a = b. // Upcasting a->move(). // cout "Moving" . // cout "Moving" .. This happens when upcasting int main() { Bird b..Static Polymorphism: Problem Call the move() method for a Bird object using a pointer to an Animal object (upcasting) int main() { Bird* b = new Bird. // Upcasting a->move().. } 5 .

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

} }. class Bird : public Animal { public: virtual void move() { cout << "Flying". // as reminder 7 .Virtual Function  When overriding a virtual method in a subclass. } }. we generally use the keyword virtual to remind ourselves that it is using dynamic binding. class Animal { public: virtual void move() { cout << "Moving".

Animal* a = b. Animal* a = &b. // cout "Flying" . Bird::move() } is called now.. // cout "Flying" .Dynamic Polymorphism Upcasting via pointer. which is more appropriate/precise/accurate in context int main() { Bird b. } 8 .. // Upcasting a->move(). // Upcasting a->move(). After making the move() method virtual int main() { Bird* b = new Bird...

// Upcasting via reference a. Animal& a = b. // cout "Flying" .. } 9 .Dynamic Polymorphism Upcasting via reference After making the move() method virtual Bird::move() is called now.move().. which is more appropriate/precise/accurate in context int main() { Bird b.

class Bird : public Animal { public: virtual void move() }. } { { cout << "Walking". } { cout << "Swimming". class Fish : public Animal { public: virtual void move() }. class Mammal : public Animal public: virtual void move() }. } 10 . Consider the following inheritance hierarchy: class Animal { public: virtual void move() }.Dynamic Polymorphism: Benefit 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. { cout << "Moving". } { cout << "Flying".

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

return 0.Dynamic Polymorphism: Benefit We can also write a loop that calls the right version of move() method for all objects in the animal inheritance hierarchy int main() { // Array of different animals Animal* a[4] = {new Animal. new Fish. i < 4. } Output: Moving Flying Swimming Walking 12 . new Bird. i++) a[i]->move(). for (int i = 0 . new Mammal}.

} }. // invoke destructor return 0. } }.Virtual Destructor   Superclass destructor should always be virtual for delete operator to call the destructors correctly Otherwise. class Sub : public Super { public: ~Sub() { cout << "Sub destroyed\n". not the subclass destructor. } Output: Super destroyed 13 . int main() { Super* p = new Sub. // Problem class Super { public: ~Super() { // non virtual cout << "Super destroyed\n". – This will cause only the base part of the object to be destroyed. // upcasting delete p. delete operator will call only superclass destructor.

} }. make destructor in superclass virtual. Output: Sub destroyed Super destroyed 14 . }  To ensure that subclass objects are destroyed properly. class Sub : public Super { public: virtual ~Sub() { cout << "Sub destroyed\n". delete p. // invoke destructor return 0. } }. int main() { // upcasting Super* p = new Sub.Virtual Destructor // Solution class Super { public: virtual ~Super() { cout << "Super destroyed\n".

Requirements for C++ Dynamic Polymorphism • There must be an inheritance hierarchy. 15 . • The superclass in the hierarchy must have a virtual method • Subclass must override superclass virtual method • There must be either a pointer or a reference to a superclass (used to invoke a virtual method).

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

} 17 . class Animal { // abstract class public: virtual void move() = 0. // ERROR: Animal is an abstract class return 0.Pure Virtual Function  A pure virtual function is a method that is initialized to zero (0) in its declaration and no implementation is given  Pure virtual function makes the class abstract and no instance of the class can be created. // pure virtual function }. int main() { Animal a.

} }. cout "Flying" return 0.Pure Virtual Function  Subclass of an abstract class must override all of the superclass pure virtual functions in order to become instantiate-able class Animal { // abstract superclass public: virtual void move() = 0. // upcasting a->move(). int main() { Animal *a = new Bird. // Fine. // pure virtual function }. } 18 . class Bird : public Animal { // concrete subclass public: virtual void move() { // overriding pure virtual function cout << "Flying".

class Animal { public: virtual void move() = 0. }. class Bird : public Animal { }. // abstract class // pure virtual function // abstract class // concrete class // overriding 19 .Pure Virtual Function  Subclass must override superclass pure virtual function in order not to become abstract itself. class Eagle : public Bird { public: virtual void move() { } }.

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