Inheritance and virtual functions
The power of inheritance in C++ comes from virtual
functions and dynamic binding.
Dynamic binding realizes the polymorphism inherent
in the static type structure of the inheritance
hierarchy in a dynamic way.
Dynamic binding is illustrated
by the example shown in slide [7-dispatching].
Virtual functions -- dispatching
class A { A
public:
virtual void operator()() {
cout << "A";
}
};
class B : public A { B
public:
virtual void operator()() {
cout << "B";
}
};
slide: Virtual functions -- dispatching
The class A defines a virtual member function
(that results in printing A) which is redefined
by a similar function in class B (which results
in printing B).
As an example of using the classes defined above,
look at the following program fragment:
A* a = new B(); (*a)(); produces B
In case the function would not have been defined as virtual,
the outcome of applying it to (a pointer to) a B object
would have been A, instead of B.
Explicit scoping
Virtual functions that are redefined in derived classes
may still access the original version defined in the base
class, as illustrated in slide [7-scoping].
Scoping may be used within a member function of the
class as well as by a client (when invoking a member
function) as illustrated below.
A* a = new B(); a->A::operator()(); (*a)(); produces ABBA
The outcome of this statement is ABBA.
Such a scoping mechanism is certainly not unique
to C++, although the notation for it is.
In Smalltalk, the expression super may be used
to access methods defined in the ancestor class,
and in Eiffel one must use a combination of
redefining and renaming to achieve this.
As a remark, I prefer the use of
operator()() when any other method name would
be arbitrary.
The attractiveness of operator()() is
that it is used as a function application
operator, as in (*a)().