Week 8: Inheritance with Virtual Functions, Inline Functions





We could say that Inheritance is the most important feature in C++

Remember that a "rule" states that a reference or pointer to a derived class object is converted to a reference or pointer to a base class object when used as an argument to a function defined as accepting a reference or a pointer to a base class object

Also, a base class pointer can point to a derived class object and that a base class reference can refer to a derived class object without an explicit type cast. But the reverse is not true, you can not have a derived class pointer or reference refer to a base class object without explicit type cast. Depending upon the class declarations, such an explicit type cast (a downcast) may or may not make sense

Today, we will talk about the "tool" that enhances the features of inheritance in C++ - Virtual function


What the Compiler Used to Knows ?


When a compiler compiles the source code file containing the function definition, it has no way of knowing what type of object will be passed to it as an argument in some other file.

The only choice the compiler can make at compile time is to match class methods to the type of reference or pointer.

This strategy is called early binding, or static binding

The term binding refers to attaching a function call to a particular function definition

In C, you have only one function per name, so the choice is obvious for the compiler

In C++, with function overloading and redefined member functions, we can have more than once function matching a given name


C++ Compiler and Dynamic Binding


A second stategy, called late binding, or dynamic binding is offered by C++

Compiler does not make decision of which class method to use

It passes responsibility to the program, which then makes a runtime decision whenever it actually executes a method function call (We will use the lab as example ...)


Dynamic Binding


We can only turn on dynamic binding for member fucntion

Use the virtual keyword in the base class

Then, redefine the function in a derived class, a program then will use dynamic binding to determine which definition to use

Once it is a virtual method, if remains virtual for all classes derived from the base class

Thus, for a given method, you only have to use the keyword virtual once, in the base class

In a nutshell, virtual member functions are created by preceding the prototype with the keyword virtual. C++ programs then use dynamic, or late, binding for virtual methods, and static, or early, binding for nonvirtual methods.

Also, for virtual functions, the type of object referred to or pointed to determines which method to a pointer or reference invokes

// arraydbv.h -- revised array class, making [] virtual
#ifndef _ARRAYDB_H_
#define _ARRAYDB_H_
#include <iostream.h>

class ArrayDb
	unsigned int size;			// number of array elements
	double * arr;                       // address of first element
	ArrayDb();                          // default constructor
	// create an ArrayDb of n elements, set each to val
	ArrayDb(unsigned int n, double val = 0.0);
	// create an ArrayDb of n elements, initialize to array pn
	ArrayDb(const double * pn, unsigned int n);
	ArrayDb(const ArrayDb & a);         // copy constructor
	virtual ~ArrayDb();                 // destructor
	unsigned int arsize() const {return size;}// returns array size
	// overloaded operators -- note use of keyword virtual
	virtual double & operator[](int i);     // array indexing
	virtual const double & operator[](int i) const; // array indexing (no =)
	ArrayDb & operator=(const ArrayDb & a);
	friend ostream & operator<<(ostream & os, const ArrayDb & a);



Notes on Binding


If dynamic binding is so cool, when even have static binding ?

Efficiency, for making dynamic binding decision, program has to keep track of what sort of object a base class pointer or reference refers to, and that entails some extra processing overhead

Second, without making a function virtual, that means we announce that it is our intention that this function not be redefined. Thus, we have control over our class design

In short, if a method in a base class will be redefined in a derived class, make it virtual. If the method should not be redefined, make it nonvirtual

// Syntax Example
class Employee
		virtual void showme(int a) const;
class Programmer : public Employee
		void showme(int a) const;


Pure Virtual Functions


C++ has a variation of the virtual function call a "pure virtual function"

It is a virtual function with a prototype but no definition


class Shape
		virtual void draw() const = 0;
		virtual double area() const - 0;

When a class declaration contains a pure virtual function, you cannot create an object of that class. So, the idea of pure virtual function only exist in base class

The base class of the pure virtual function then is called abstract base class (ABC)

For example, Shape will be a good candiate for ABC, since there is actually no "Shape" in the real world. There might be Triangle, Rectangle or Circle ... but not Shape

As a matter of fact, ABC can be used to describes an interface in terms of pure virtual functions, can a class derived from an ABC uses regular virtual functions to implement the interface in terms of the properties of the particular derived class

Full example in lab


Inline Functions


This is a programming short cut in a sense (this topic is included for completion, it is not directly related to inheritance, however, quite a few people use this technique for quick virtual member function)

Inline function is a C++ enhancement designed to speed up programs

Use does not need to change the way he/she codes, C++ compiler will incorporate them (inline functions) into a program

When normal function is called, processor will usually save all the register information, memory information, then jump to the location of the function (at that point, it will be some session of machine code)

When the normal function finish execution, it will then restore all the registers and memory information, then jump back to the point in the program after the function execution

With C++ inline function, C++ compiler compiles the function "in line" with the other code in the program

The compiler replaces the function call with the corresponding function code in the machine language level, so no jump of function call is necessary

Run a little faster depend on system

BUT, there is a memory penalty. If a program calls an inline function ten times, then the program winds up with ten copies of the function inserted into the code

Use inline function selectively in order to take the advantages


Syntax Note


To use inline function, we have to do

Preface the function definition with the keyword inline

Place the function definition above all the functions that call it

OR define the function in the header files (as in the lab)

Note that you have to place the entire definition (meaning the function header and all the function code), not just the prototype, above the other functions

The compiler does not have to honor your request to make a function inline. It may decide the function is too large or notice that it calls itself (recursion is not allowed for inline functions), or the feature may not be implemented for your particular compiler


// inline.cpp -- use an inline function
#include <iostream.h>

// an inline function must be defined before first use
inline double square(double x) { return x * x; }

int main(void)
	double a, b;
	double c = 13.0;

	a = square(5.0);
	b = square(4.5 + 7.5);   // can pass expressions
	cout << "a = " << a << ", b = " << b << "\n";
	cout << "c = " << c;
	cout << ", c squared = " << square(c++) << "\n";
	cout << "Now c = " << c << "\n";
	return 0;





Shape.hpp (class definition and implementation)

Circle.hpp (class definition and implementation)

Rectangle.hpp (class definition and implementation)

week8.cpp (source code)

week8.out (output)