Top Banner
1 Department of Computer Science and Engineering, HKUST HKUST Summer Programming Course 2008 Inheritance ~ Writing Reusable Code
105

HKUST Summer Programming Course 2008

Jan 24, 2016

Download

Documents

jace

HKUST Summer Programming Course 2008. Inheritance ~ Writing Reusable Code. Overview. Introduction Derived Object Initialization Polymorphic Substitution Principle Member Access Control public, protected and private Inheritance Polymorphism and Binding Virtual Functions - PowerPoint PPT Presentation
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: HKUST Summer Programming Course 2008

1Department of Computer Science and Engineering, HKUST

HKUST SummerProgramming Course 2008

Inheritance

~ Writing Reusable Code

Page 2: HKUST Summer Programming Course 2008

2

Overview Introduction Derived Object Initialization Polymorphic Substitution Principle Member Access Control public, protected and private Inheritance Polymorphism and Binding Virtual Functions Overriding and Overloading Virtual Destructor Abstract Base Class (ABC)

Page 3: HKUST Summer Programming Course 2008

3Department of Computer Science and Engineering, HKUST

Inheritance

Introduction

Page 4: HKUST Summer Programming Course 2008

4

Introduction - Inheritance

Inheritance is a key feature of an object-oriented language. It is the ability to define new classes using existing classes

as a basis. The new class (derived class) inherits all data members

and member functions of the classes on which it is based (base class).(Exceptions: constructors, destructor, friend functions

and the assignment operator are not inherited.)

Page 5: HKUST Summer Programming Course 2008

5

Introduction - Inheritance

In addition, the new class can have additional data members and member functions specific to it. The new class only has to implement the behavior that is

different from existing classes.

SYNTAXclass <derived-class-name> : <access> <base-class-name>{

// body of class

};

where <access> must be either public, private or protected which determines the access status of base-class members inside the derived class.

Page 6: HKUST Summer Programming Course 2008

6

Example – Person Class

First, let us look at an example. We begin with a class Person:class Person{

private:

char name[30];

public:

Person();

const char* get_name() const;

void set_name(const char* n);

void output_info() const;

};

Page 7: HKUST Summer Programming Course 2008

7

Example – Person Class

Person::Person(){

name[0] = ‘\0’;

}

const char* Person::get_name() const{

return name;

}

void Person::set_name(const char* n){

strcpy(name, n);

}

void Person:output_info() const{

cout << “Name: “ << name << endl;

}

Page 8: HKUST Summer Programming Course 2008

8

Example – Employee Class

If we want to described person in employment, we can define a new class using Person as a basis:class Employee : public Person{

private:

int salary;

public:

Employee() : salary(0) {}

int earns() const;

void change_salary(int new_salary);

void output_info() const;

};

Page 9: HKUST Summer Programming Course 2008

9

Example – Employee Class

int Employee::earns() const{

return salary;

}

void Employee::change_salary(int new_salary){

salary = new_salary;

}

Page 10: HKUST Summer Programming Course 2008

10

Inherited Data Members

We say that class Employee is a derived class from Person and that class Person is a direct base class of Employee.

A derived class inherits all the data members from the base class.

Person p;

Employee e;name

p

name

e

salary

Page 11: HKUST Summer Programming Course 2008

11

Inherited Member Functions

Member functions can also be inherited. An object of class Employee will have the member functions

get_name, set_name, output_info, earns and change_salary.

We can also write statements

p.set_name(“David”);

p.output_info();

e.set_name(“Andrew”);

e.change_salary(20000);

int s = e.earns();

e.output_info();

Page 12: HKUST Summer Programming Course 2008

12

Inherited Member Functions

But we could not writep.change_salary(1000); or

p.earns();

Since these member functions do not exist for class

Person.

Page 13: HKUST Summer Programming Course 2008

13

Direct and Indirect Inheritance

A derived class can also have derived classes. For instance, we declare a class Programmer that is

a derived class of class Employee.class Programmer : public Employee{

private: // fam_lang indicates a programmer’s

// most familiar programming language. char fam_lang[10];public: Programmer(); const char* read_fam_lang() const; void change_fam_lang(const char* f);

};

Page 14: HKUST Summer Programming Course 2008

14

Direct and Indirect Inheritance

Programmer::Programmer(){

fam_lang[0] = ‘\0’;

}

const char* Programmer::read_fam_lang() const{

return fam_lang;

}

void Programmer::change_fam_lang(const char* f){

strcpy(fam_lang,f);

}

Page 15: HKUST Summer Programming Course 2008

15

Direct and Indirect Inheritance

In last example, the class Person and Employee are base classes of Programmer. The class Employee is a direct base class. The class Person is an indirect base class.

We say, the class Programmer is directly derived from class Employee and indirectly derived from class Person.

An object of class Programmer inherits data members and member functions from all of its base classes.

Page 16: HKUST Summer Programming Course 2008

16Department of Computer Science and Engineering, HKUST

Inheritance

Derived Object Initialization

Page 17: HKUST Summer Programming Course 2008

17

Derived Class Object Initialization

Objects that belong to a derived class can be initialized like other objects with the aid of constructors.

When a constructor for the derived class is called for this purpose The data members that have been inherited from the base

class must also be initialized. This is done by calling one of the constructors for the base class.

The data members specific to the derived class will be in turn initialized by the constructor.

Page 18: HKUST Summer Programming Course 2008

18

Example - Object Initialization

Consider the following example. Originally, the class Person only had one constructor, a

default constructor, now we add another one.

class Person{

private:

char name[30];

public:

Person();

Person(const char* n);

// ...

};

Page 19: HKUST Summer Programming Course 2008

19

Example - Object Initialization

Person::Person(){

name[0] = ‘\0’;

}

Person::Person(const char* n){

strcpy(name, n);

}

The new constructor is called when we make the declaration:Person p(“David”);

Page 20: HKUST Summer Programming Course 2008

20

Example - Object Initialization

class Employee : public Person{

private:

int salary;

public:

Employee() : salary(0) {}

Employee(const char* n, int l)

: Person(n), salary(1) {}

}; In the second constructor of Employee class, we have used an

initialization list to initialize data members of the object. The expression Person(n) is a call of the constructor for the

base class Person to initialize those members in the base class.

Page 21: HKUST Summer Programming Course 2008

21

Example - Object Initialization

Let us now see what happens if we create and initialize an object of class Employee using the following declaration.Employee e1(“Peter”, 20000); The constructor Employee(const char* n, int l) :

Person(n), salary(1){} is called. Because the parameter n has the value “Peter”, we get the

expression Person(“Peter”) in the initialization list. As a result, the constructor Person(const char* n) is

called and the data member name initialized. Then the data member salary is initialized to 20000.

Page 22: HKUST Summer Programming Course 2008

22

Example - Object Initialization

Note: The constructor for the base class is always called before the data members specific to the derived class are initialized

and the statements to be executed inside the braces of derived

class are executed. In the constructor,

Employee(const char* n, int l)

: Person(n), salary(1) {} Person(n) in written in front of salary(1) in the

initialization list but the order doesn’t matter. The base class constructor is always called first.

Page 23: HKUST Summer Programming Course 2008

23

Example - Object Initialization

Let’s consider another case. We initialize an Employee object by making the declarationEmployee e2;

using the the default constructor for class Employee, i.e. Employee() : salary(0) {} An initialization list is also used in this constructor but there is

no expression for the base class Person. Since a constructor for a base class must always be called, a call of the base class default constructor is generated automatically.

In other words, the default constructor for class Employee will be implicitly converted to

Employee() : Person(), salary(0) {}

Page 24: HKUST Summer Programming Course 2008

24

Example: Order of Construction/Destruction

#include <iostream>

using namespace std;

class A{

public:

A() { cout << “A’s constructor” << endl; }

~A() { cout << “A’s destructor” << endl; }

};

class B{

public:

B() { cout << “B’s constructor” << endl; }

~B() { cout << “B’s destructor” << endl; }

};

Page 25: HKUST Summer Programming Course 2008

25

Example: Order of Construction/Destruction

class C : public B{

private:

A a;

public:

C() { cout << “C’s constructor” << endl;}

~C() { cout << “C’s destructor” << endl; }

};

int main(){

C c;

return 0;

}

Output:B’s constructor

A’s constructor

C’s constructor

C’s destructor

A’s destructor

B’s destructor

Page 26: HKUST Summer Programming Course 2008

26Department of Computer Science and Engineering, HKUST

Inheritance

Polymorphic Substitution Principle

Page 27: HKUST Summer Programming Course 2008

27

Polymorphic Substitution Principle

The single most important rule in OOP with C++ is:

Inheritance means “is a”.

If class D (the derived class) inherits from class B (the base class) Every object of type D is also an object of type B, but NOT vice-

versa. B is more general concept, D is more specific concept. Where an object of type B is needed, an object of type D can be

used instead.

Base class

Derived class

“ is-a” relationship

Page 28: HKUST Summer Programming Course 2008

28

Polymorphic Substitution Principle

In C++, using our Person and Employee examples, where Employee is derived from Person, this means:

It is also known as “Liskov Substitution Principle”.

Any Function That Expects as Argument of Type

Will Also Accepts

Person Employee

pointer to Person pointer to Employee

Person reference Employee reference

Page 29: HKUST Summer Programming Course 2008

29

Example – Substitution in Arguments

void dance(const Person& p); // Anyone can dancevoid work(const Employee& e); // Only Employees work

void dance(Person* p); // Anyone can dancevoid work(Employee* e); // Only Employees work

int main(){Person p;Employee e;dance(p); /* OK */ dance(e); /* OK */work(e); /* OK */ work(p); /* ERROR */dance(&p); /* OK */ dance(&e); /* OK */work(&e); /* OK */ work(&p); /* ERROR */return 0;

}

Page 30: HKUST Summer Programming Course 2008

30

Extending Class Hierarchies

We can easily add new classes to our existing class hierarchy of Person, Employee, and Programmer. New classes can immediately benefit from all functions that

are available for their base classes. e.g. void dance(const Person& p); will work

immediately for a new class type Programmer which indirectly inherits Person, even though this type of object was unknown when dance(const Person& p) was designed and written.

In fact, it is not even necessary to recompile the existing code: It is enough to link the new class with the object code for Person and dance(const Person& p).

Page 31: HKUST Summer Programming Course 2008

31

Slicing

A derived class often holds more information (i.e. data members) that its base class.

Thus, if we assign an instance of the derived class to a variable of type base class, there is not enough space to store the extra information, and it is sliced off. This is rarely desirable

Note that this does not happen when you use pointers or references to objects.

Page 32: HKUST Summer Programming Course 2008

32

Example - Slicingclass Person{ class Employee : public

Person{private: private:

char name[30]; int salary;public: public:

// ... // ...}; };

int main(){Employee e;Person p;Person* pp1 = &e; // okayPerson* pp2 = new Employee; // okayp=e; // slicing occurs, salary is not copiedreturn 0;

}

Page 33: HKUST Summer Programming Course 2008

33

Name Conflicts

We are allowed to use the same name of a member in a derived class as in a base class.class Employee : public Person{

private: int salary; char name[30];public: Employee() : salary(0) {} int earns() const; void change_salary(int new_salary); void output_info() const;

}; A new data member “name” in class Employee hides the

data member “name” of class Person.

name (Person)Employee

salary

name

Page 34: HKUST Summer Programming Course 2008

34

Name Conflicts

We can do the same thing for member functions. When a derived class define a member function with the

same name as a base class member function, it overrides the base class member function.

This is necessary if the behaviour of the base class member function is not good enough for derived classes.

Page 35: HKUST Summer Programming Course 2008

35

Name Conflicts

For instance, a member function with name output_info is declared in class Employee. This will hide the member function output_info which was

declared in class Person. If we write e.output_info(), where e is an object of type

Employee, the output_info that was declared in class Employee will be called.

Page 36: HKUST Summer Programming Course 2008

36

Name Conflicts The definition of output_info for class Employee looks

like this:void Employee::output_info() const{

Person::output_info();cout << “Salary: ” << salary << endl;

} The second line is interesting. It shows how we can access a

hidden member. To access a hidden member with the name, say m that has

been declared in a base class B, we write B::m. Here we write Person::output_info(), which means

that the output_info declared in base class Person will be called.

Page 37: HKUST Summer Programming Course 2008

37

Example – Name Conflicts

class B{

private:

int x, y;

public:

B() : x(1), y(2){

cout << “Base class constructor” << endl;

}

void f(){

cout << “Base class: ” << x << “ , ”

<< y << endl;

}

};

Page 38: HKUST Summer Programming Course 2008

38

Example – Name Conflicts

class D : public B {private: float x, y;public: D() : x(10.0), y(20.0){

cout << “Derived class constructor” << endl; } void f(){

cout << “Derived class: ” << x << “ , ” << y << endl; B::f();

}};

Page 39: HKUST Summer Programming Course 2008

39

Example – Name Conflictsvoid smart(B* z){ cout << “Inside smart(): ”; z->f();}int main(){

B base; B* b = &base;D derive; D* d = &derive;base.f();derive.f();b = &derive;b->f();smart(b);smart(d);return 0;

}

Output:Base class constructor

Base class constructor

Derived class constructor

Base class: 1 , 2

Derived class 10 , 20

Base class: 1 , 2

Inside smart(): Base class: 1 , 2

Inside smart(): Base class: 1 , 2

Page 40: HKUST Summer Programming Course 2008

40Department of Computer Science and Engineering, HKUST

Inheritance

Member Access Control

Page 41: HKUST Summer Programming Course 2008

41

Member Access Control

So far, we have seen how, in a class definition, we can use the access specifiers public and private to indicate whether the members of a class will be accessible outside the class.

There is a third possibility, a sort of compromise between public and private, the reserved word protected.

Page 42: HKUST Summer Programming Course 2008

42

Member Access Control

A class definition can have the form

class C{

public:

// declaration of visible members

protected:

// declaration of protected members

private:

// declaration of private members

}; If no access specifier is written, by default all the members

will be private.

Page 43: HKUST Summer Programming Course 2008

43

Member Access Control

If a member is declared in a class C and is private (declared in the private section), it can only be used by the member functions in C and by the friends of class C.

If a member is declared in a class C and the member is protected (declared in the protected section), it can only be used by the member functions in C, friends of C and member functions and friends of classes derived from C.

If a member is public (declared in public section), it can be used everywhere, without restriction.

Page 44: HKUST Summer Programming Course 2008

44

Example – Protected Data Members A protected member is accessible within the inheritance

hierarchy but not outside it. We will use class Clock to give an example.

class Clock{protected: int h, m, s;public: Clock(); Clock(int hour, int min, int sec); void set(int hour, int min, int sec); int get_hour() const { return h; } int get_min() const { return m; } int get_sec() const { return s; } void write(bool write_sec = true) const; void tick();

};

Page 45: HKUST Summer Programming Course 2008

45

Example – Protected Data Members

An alarm clock has the same properties as an ordinary clock but we have added the possibility of having an alarm at a specific time.class Alarm_Clock : public Clock{

protected:

int ah, am, as;

public:

void set_alarm(int hour, int min, int sec);

void tick();

};

Page 46: HKUST Summer Programming Course 2008

46

Example – Protected Data Members The member function set_alarm with which we indicate

when the alarm is to ring.void set_alarm(int hour, int min, int sec){

ah = hour;am = min;as = sec;

} The definition of the new variant of the function tick is

most interesting here.void tick(){

Clock::tick(); if(h == ah && m==am && s==as) cout << “\a\a\a”;

}

Page 47: HKUST Summer Programming Course 2008

47

Example – Protected Data Members

In member function tick(), First we call the function tick, which was declared in the

base class Clock. This moves the clock forward by one second and then check to see whether it is time for the alarm to ring.

But for this to happen, we must have access to the data members of base class Clock (i.e. h, m, s).

If h, m and s had been private members in the base class, this would not have been possible, but by declaring them as protected, we will have access to them in derived class Alarm_Clock.

Page 48: HKUST Summer Programming Course 2008

48

protected vs. private

As a rule, we said that if a class is constructed and intended to make it the base class for various derived classes, then its data members should be declared as protected rather than as private.

So why not always use protected instead of private? Because protected means that we have less encapsulation:

Re-member that all derived classes can access protected data members of the base class.

Assume that later you decided to change the implementation of the base class having the protected data members.

If it is protected, we have to go through all derived classes and change them.

Page 49: HKUST Summer Programming Course 2008

49

protected vs. private

In general, it is preferable to have private members instead of protected members.

Use protected only where it is really necessary. Private is the only category ensuring full encapsulation.

This is particularly true for data members, but it is less harmful to have protected member functions.

In our example, there is no reason at all to make h, m, s protected, since we can access them through the public member functions.

Page 50: HKUST Summer Programming Course 2008

50

Example – tick() using Public Functions Only

class Alarm_Clock : public Clock{protected:

int ah, am, as;public:

// ...void tick(){ Clock::tick(); // use public member functions to access // data members h, m and s. if(read_hour() == ah && read_min() == am && read_sec() == as) // \a represents alert sound (bell) cout << “\a\a\a”;}

};

Page 51: HKUST Summer Programming Course 2008

51Department of Computer Science and Engineering, HKUST

Inheritance

public, protected, private Inheritance

Page 52: HKUST Summer Programming Course 2008

52

public, protected, private Inheritance

When a class D is derived from a base class B, then as we have seen, it will inherit the members of B.

When then accept the inherited members as members of D and, just as for other members, the accessibility of the inherited members must be specified.

We do this by writing one of the keywords public, protected or private in front of the name of the based class concerned.

Page 53: HKUST Summer Programming Course 2008

53

public, protected, private Inheritance

We can write one of the following:class D : public B{ // public inheritance

// ...

};

class D : protected B{ // protected inheritance

// ...

};

class D : private B{ // private inheritance

// ...

};

We call these public, protected and private inheritance, respectively.

Page 54: HKUST Summer Programming Course 2008

54

public, protected, private Inheritance

We can give the following rules, where B is the base class and D the derived class. Private members of B are never visible anywhere but in B,

neither in D nor anywhere outside B. If private inheritance is involved, then all the members of B

which are protected or public will be private in D. If protected inheritance is involved, then all the members of B

which are protected or public will be protected in D. If public inheritance is involved, then all the members of B

which are protected will also be protected in D and all the members of B which are public will also be public in D.

Page 55: HKUST Summer Programming Course 2008

55

Example – public, protected, private Inheritance

We first declare a base class B with three derived classes D1, D2 and D3.

class Base{

public:

int i1;

protected:

int i2;

private:

int i3;

};

class D1 : private Base{

void f();

};

class D2 : protected Base{

void g();

};

class D3 : public Base{

void h();

};

Page 56: HKUST Summer Programming Course 2008

56

Example – public, protected, private Inheritance

None of the derived classes (D1, D2 & D3) has access to member i3 in the base class B.

But all three of the derived classes can access members i1 and i2 internally. In the definition of the member function f (the same thing

applies to g and h) we will get, e.g.:

void D1::f(){

i1 = 0; // OK

i2 = 0; // OK

i3 = 0; // ERROR

}

Page 57: HKUST Summer Programming Course 2008

57

Example – public, protected, private Inheritance

Access for i1, i2 and i3 outside the three derived classes is shown in the following program.

int main(){

Base b;

b.i1 = 0; // OKb.i2 = 0; // ERROR!

b.i3 = 0; // ERROR!

D1 d1;

d1.i1 = 0; // ERROR!

d1.i2 = 0; // ERROR!

d1.i3 = 0; // ERROR!

D2 d2; d2.i1 = 0; // ERROR! d2.i2 = 0; // ERROR! d3.i3 = 0; // ERROR! D3 d3; d3.i1 = 0; // OK d3.i2 = 0; // ERROR! d3.i3 = 0; // ERROR! return 0;}

Page 58: HKUST Summer Programming Course 2008

58

Summary - Different Types of Inheritance

The various types of inheritance control the highest accessibility of the inherited data member and functions.

Public inheritance preserves the original accessibility of inherited members:

public publicprotected protected

private private

Protected inheritance affects only public members and renders them protected:

public protected

protected protected

private private

Page 59: HKUST Summer Programming Course 2008

59

Summary - Different Types of Inheritance

Private inheritance renders all inherited members private:

public private

protected private

private private

Public inheritance implements the “is-a” relationship. Private inheritance is similar to “has-a” relationship. Public inheritance is the most common type of inheritance.

Page 60: HKUST Summer Programming Course 2008

60Department of Computer Science and Engineering, HKUST

Inheritance

Polymorphism and Binding

Page 61: HKUST Summer Programming Course 2008

61

Polymorphism and Binding

The term polymorphism (for the Greek meaning have “multiple forms”) refers to a programming language's ability to process objects differently depending on their data type or class.

In C++, polymorphism can be achieved in several ways via: function overloading and operator overloading function templates virtual functions with dynamic binding (i.e. run-time binding)

We will talk about this now.

Page 62: HKUST Summer Programming Course 2008

62

Polymorphism and Binding

Overloading and template are two forms of what is called polymorphism (having multiple forms) in the context of programming.

This means that a program construct which has a certain appearance can mean different things (for example, calls to different functions) depending on the types of the operands involved.

Binding means deciding exactly which form is appropriate.

Page 63: HKUST Summer Programming Course 2008

63

Static or Early Binding

For both overloading and templates, binding occurs during compilation, and this is know as static, or early binding. Static binding (association) binds a function name to the

appropriate function by a static analysis of the code at compile time based on the declared type of the object used in the call.

The fact that the pointer can point to, (or the reference is actually a reference of) an object of a derived class is not considered.

Page 64: HKUST Summer Programming Course 2008

64

Dynamic or Late Binding

Alternatively, dynamic or late binding can occur, and this takes place during program execution (i.e. runtime). It takes place in connection with inheritance. In C++, we use what we called virtual functions when we

want to have dynamic binding.

Page 65: HKUST Summer Programming Course 2008

65Department of Computer Science and Engineering, HKUST

Inheritance

Virtual Functions

Page 66: HKUST Summer Programming Course 2008

66

Virtual Functions

A virtual function is a member function that is declared using the virtual keyword in the class definition (but not in the member function implementation, if it is outside the class).

A class which contains at least one virtual function, or which inherits a virtual function, is called a polymorphic class.

To demonstrate the concepts of polymorphism, dynamic binding and virtual functions, we will define a group of classes that describe different kinds of vehicle.

Page 67: HKUST Summer Programming Course 2008

67

Example – Class Hierarchy

VehicleVehicle

Motor_VehicleMotor_Vehicle

Private_CarPrivate_Car BusBus TruckTruck

Mini_BusMini_Bus

Page 68: HKUST Summer Programming Course 2008

68

Example – Virtual Functions

class Vehicle{public: virtual void output_info();

};class Motor_Vehicle : public Vehicle{

public:Motor_Vehicle(char* no) { strcpy(car_num, no); }char* number() { return car_num; }// output_info() here is automatically virtual// even no virtual keyword is writtenvoid output_info();

protected:char car_num[8];

};

Page 69: HKUST Summer Programming Course 2008

69

Example – Virtual Functions

class Private_Car : public Motor_Vehicle{

public:

Private_Car(char* no, int n)

: Motor_Vehicle(no), num_seats(n) {}

// output_info() here is automatically virtual

// even no virtual keyword is written

void output_info();

protected:

int num_seats;

};

Page 70: HKUST Summer Programming Course 2008

70

Example – Virtual Functions

class Bus : public Motor_Vehicle{

public:

Bus(char* no, int n, bool a)

: Motor_Vehicle(no), num_passengers(n), has_aircond(a) {}

// output_info() here is automatically virtual

// even no virtual keyword is written

void output_info();

protected:

int num_passengers;

bool has_aircond;

};

Page 71: HKUST Summer Programming Course 2008

71

Example – Virtual Functions

class Mini_Bus : public Bus{

public:

Mini_Bus(char* no, int n, bool a)

: Bus(no, n, a) {}

};

class Truck : public Motor_Vehicle{

public:

Truck(char* no, int l)

: Motor_Vehicle(no), max_load(l) {}

protected:

int max_load;

};

Page 72: HKUST Summer Programming Course 2008

72

Example – Virtual Functions

The root class is Vehicle since all the other classes are either directly or indirectly derived from this class.

The function output_info is declared in the class Vehicle. Note that the word virtual stands in the declaration. This means that output_info is a virtual function.

All the classes, except the class Mini_Bus, each have their own variant of the member function output_info, and all these variants will be virtual functions.

Page 73: HKUST Summer Programming Course 2008

73

Example – Virtual Functions The word virtual does not have to be repeated in all the

derived classes. Once a method is declared virtual in the base class, it is automatically virtual in all directly or indirectly derived classes. Even though it is not necessary to use the virtual keyword in the

derived class, it is good style to do so, because it improves the readability.

class Motor_Vehicle : public Vehicle{protected:

char car_num[8];public:

virtual void output_info();// ...

};

Page 74: HKUST Summer Programming Course 2008

74

Example – Virtual Functions

The class Mini_Bus will inherit the variant of output_info from class Bus.

The definitions of the different variants of output_info are as follows.

void Vehicle::output_info(){

cout << “A vehicle” << endl;

}

void Motor_Vehicle::output_info(){

cout << “A motor vehicle” << endl;

cout << “Car no: “ << car_num << endl;

}

Page 75: HKUST Summer Programming Course 2008

75

Example – Virtual Functionsvoid Private_Car::output_info(){

Motor_Vehicle::output_info();cout << “A private car” << endl;cout << num_seats << “ seats” << endl;

}void Bus::output_info(){

Motor_Vehicle::output_info();cout << “A bus” << endl;cout << num_passengers << “ passengers” << endl;if(has_aircond) cout << “Has air-conditioning” << endl;

}void Truck::output_info(){

Motor_Vehicle::output_info();cout << “A truck” << endl;cout << max_load << “kg maximum load” << endl;

}

Page 76: HKUST Summer Programming Course 2008

76

Example – Virtual Functions

Suppose that we now declare three variables:

Private_Car pc(“ABC 555”, 5);

Truck tr(“DEF 222”, 10000);

Mini_Bus mb(“GHI 333”, 10, true);

If we make the callpc.output_info(); // static binding

We get written outA motor vehicle

Car no: ABC 555

A private car

5 seats

Page 77: HKUST Summer Programming Course 2008

77

Example – Virtual Functions

In last example, there is a call to the variant of output_info that was declared in class Private_Car.

Note that this function in turn calls the variant of output_info belonging to Motor_Vehicle.

These are examples of static binding. Since the type of variable pc is known at compilation, the compiler can decide which variant of output_info to be called.

To demonstrate dynamic binding, we declare a pointer fp:Vehicle* fp;

Page 78: HKUST Summer Programming Course 2008

78

Example – Virtual Functions

Now let the pointer fp point to a truck by making an assignment:

fp = &tr; If a call is made

fp->output_info(); // dynamic binding

then it will not be possible to decide at compilation what kind of object fp points to. Thus, at compilation it is not possible to decide which

variant of the function output_info should be called. We will therefore get dynamic binding.

Page 79: HKUST Summer Programming Course 2008

79

Example – Virtual Functions

When the program is executed, there is a check to see what kind of object fp points to at that moment, followed by a call to the function for it.

As fp is currently pointing to a Truck object, the output_info for Truck class will be called and have the following output.A motor vehicle

Car no: DEF 222

A truck

10000 kg maximum load

Page 80: HKUST Summer Programming Course 2008

80

Example – Virtual Functions

If, instead, we had executed the statementsfp = new Private_Car(“AAA 333”, 4);

fp->output_info(); // dynamic binding

cout << endl;

fp = &mb;

fp->output_info(); // dynamic binding

we would have had

this output:

Output:A motor vehicleCar no: AAA 333A private car4 seats

A motor vehicleCar no: GHI 333A bus10 passengersHas air-conditioning

Page 81: HKUST Summer Programming Course 2008

81

Static and Dynamic Type of Expression

In C++, we differentiate between static and dynamic type of an expression.

The static type will depend entirely on how the type of the expression has been declared in the program and it is determined at compilation and is not changed while the program is executed.

For instance, the static type for the expression *fp is Vehicle, since it was stated in the declaration of fp that it should have this type.

Vehicle *fp;

Page 82: HKUST Summer Programming Course 2008

82

Static and Dynamic Type of Expression

The dynamic type for an expression is decided by the actual value of the expression and can be changed during execution.

The expression can contain a pointer or a reference. For instance, if the pointer fp happens to point to a Bus,

the dynamic type for the expression *fp will be Bus.

Page 83: HKUST Summer Programming Course 2008

83

Type of Expression and Virtual Functions

When a virtual function is called, the dynamic type of the actual object will determine which function is to be called.

When an ordinary, non-virtual function is called, the static type of the actual object determines which function will be called.

Generally, this means that if we are to have dynamic binding, we must have expressly declared the function involved as virtual.

Page 84: HKUST Summer Programming Course 2008

84

Type of Expression and Virtual Functions

If we had left out the word virtual in the declaration of output_info in class Vehicle, there would not have been dynamic binding in the function calls.

Then, in the call fp->output_info(), the static type of fp would have been decisive. This would have meant the variant of output_info

declared Vehicle would have been called irrespective of what kind of object fp had happened to point to.

Page 85: HKUST Summer Programming Course 2008

85

Summary of Dynamic Binding

The actual function must be declared as virtual in the base class:

virtual <return_type> <func_name)(<parameters>);

It is re-declared in the derived classes with exactly the same parameters and return type.

The word virtual does not have to be repeated, but is a good practice to put it in.

Static type – The type an expression has according to the declarations.

Dynamic type – The type an expression has at execution.

Page 86: HKUST Summer Programming Course 2008

86

Summary of Dynamic Binding

Call a virtual function:p->f(...); // p is a pointer to the base type

r.f(...); // r is a reference to the base type Which function is to be called, is determined by the dynamic

type of p and r.

Page 87: HKUST Summer Programming Course 2008

87Department of Computer Science and Engineering, HKUST

Inheritance

Overriding vs. Overloading

Page 88: HKUST Summer Programming Course 2008

88

Overriding and Virtual Functions

Recall that it is necessary for derived classes to override the base class member function if the base class method is not good enough. This is called “Member Function Overriding”.

The designer of the base class must realize that this will be necessary and declare those member functions as virtual. Overriding is not possible if the member function is not

virtual.

Page 89: HKUST Summer Programming Course 2008

89

Overriding and Virtual Functions

The designer of the base class must distinguish carefully between two kinds of member functions: If the method works exactly the same for all derived classes,

it should not be a virtual member function. If the precise behaviour of the member function depends on

the type of object, it should be a virtual function. However, derived classes have to be careful in

implementing this member function because of substitution principle.

Page 90: HKUST Summer Programming Course 2008

90

Overriding and Virtual Functions

This “effect” (meaning) of calling the derived class member function must be the same as for the base class member function.

For example, output_info should not be a member function that does something completely different.

Overriding is for specializing a behaviour, not changing the semantics.

The compiler can only check that overriding is done syntactically correct, not whether the semantics of the method are preserved.

Page 91: HKUST Summer Programming Course 2008

91

Overriding vs. Overloading

Overloading: allows us to use functions or member functions with the same name, but different arguments. The decision on which function to use is done by the

compiler when the program is compiled. This is static binding.

Page 92: HKUST Summer Programming Course 2008

92

Overriding vs. Overloading

Overriding: allows a derived class to provide a different implementation for a member function declared in the base class. Overriding is only possible with inheritance and dynamic

binding – without inheritance there is no overriding. The decision which method to use is done at the moment

that the member function is called, i.e. at runtime. This is dynamic binding.

It only applies to member functions, not global functions.

Page 93: HKUST Summer Programming Course 2008

93Department of Computer Science and Engineering, HKUST

Inheritance

Virtual Destructor

Page 94: HKUST Summer Programming Course 2008

94

Virtual Destructor

Destructor can be defined as a virtual function and this can be very useful.

To demonstrate a situation where we need to use virtual destructor, we use the following example.class Student : public Person{

private:

bool elite;

char* society;

public:

Student(char* n, char* k);

~Student();

};

Page 95: HKUST Summer Programming Course 2008

95

Virtual Destructor

We let the data member society to be a pointer to a text string which contains the name of the student’s club.

This means that space for the text string must be allocated dynamically each time a new Student is created.Student::Student(char* n, char* k) : Person(n){

society = new char[strlen(k)+1];

strcpy(society, k);

}

Page 96: HKUST Summer Programming Course 2008

96

Virtual Destructor

Since dynamic memory space is allocated in the constructor, we must also have a destructor that de-allocates this memory space when it is no longer needed.Student::~Student(){

delete [] society;

}

A test program that uses the class Student is given below for demonstration purpose.

Page 97: HKUST Summer Programming Course 2008

97

Virtual Destructor

int main() {

Person *p1;

p1 = new Student(“John”, “Drama Society”);

delete p1;

}

delete p1 calls the Person destructor. The Student destructor is not executed.

The Student object itself is removed from the heap, but the resources it owns are not deleted.

Therefore, there is memory leak in this code. The text string “society” for John is not destructed.

Page 98: HKUST Summer Programming Course 2008

98

Virtual Destructor

The solution is to switch on dynamic binding, in this case for destructor:class Person{

public:

// ...

virtual ~Person() {};

// ...

};

The new destructor does nothing as no dynamic memory space was allocated for class Person.

Now, delete p1 correctly calls the Student destructor if p1 points to a Student object.

Page 99: HKUST Summer Programming Course 2008

99

Virtual Destructor

From this example, we learn that we should always declare a virtual destructor in a class if this class is intended to be the base class for other classes.

If this is not done, programs can be created that that consume memory without releasing it, and this can lead to serious execution errors.

Page 100: HKUST Summer Programming Course 2008

100Department of Computer Science and Engineering, HKUST

Inheritance

Abstract Base Class (ABC)

Page 101: HKUST Summer Programming Course 2008

101

Abstract Base Class (ABC)

The member function output_info for class Vehicle is really quite meaningless because the only thing that happens when it is used, is that it writes out the text “A vehicle”. We define it because we wanted to be sure that there would

be a function output_info for every object of a class derived from class Vehicle.

But we do not have to invent a “meaningless” member function for a base class in order to be assured that all the objects will have this function. In this case, what should we do?

Page 102: HKUST Summer Programming Course 2008

102

Abstract Base Class (ABC)

We can declare the output_info function as a pure virtual function.class Vehicle{

public:

// pure virtual function

virtual void output_info() = 0;

}; A pure virtual function will not be implemented (defined), thus

we will not be able to call it. It is the initialization to 0 that indicates that output_info is

a pure virtual function (i.e. no implementation will be provided).

Page 103: HKUST Summer Programming Course 2008

103

Abstract Base Class (ABC)

A class that contains at least one virtual function is called an abstract base class (ABC).

Since a pure virtual function cannot be called, it is not allowed to create objects of an abstract class. Thus we cannot declare variables of type Vehicle now.

Objects can only be created for those derived classes that inherit the abstract base class and provide “true” implementation for those pure virtual functions.

Page 104: HKUST Summer Programming Course 2008

104

Example

class Bike : public Vehicle{

public:

void output_info(){

cout << “This is a bike” << endl;

}

};

Vehicle v; // error! Vehicle is an abstract base class

Bike b; // okay

If a derived class (for instance, Bike) does not implement the pure virtual functions, then the derived class is also abstract (i.e. a pure virtual function), and there cannot create objects of that type.

Page 105: HKUST Summer Programming Course 2008

105

Final Remark of ABC

An abstract base class cannot be used as an argument type (called by value) a function return type (returned by value) the type of an explicit conversion

However, pointers and references to an ABC can be declared.Vehicle* v; // okay

Bike b; // okay

Vehicle& vr = b; // okay