File: intro.yo

package info (click to toggle)
c%2B%2B-annotations 7.2.0-1
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 11,484 kB
  • ctags: 2,902
  • sloc: cpp: 15,844; makefile: 2,997; ansic: 165; perl: 90; sh: 29
file content (67 lines) | stat: -rw-r--r-- 4,406 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
As we have seen in chapter ref(INHERITANCE), bf(C++) provides the tools to
derive classes from base classes, and to use base class pointers to address
derived objects. As we've also seen, when using a i(base class pointer) to
address an object of a i(derived class), the type of the pointer determines
which i(member function) will be used. This means that a tt(Vehicle *vp),
pointing to a tt(Truck) object, will incorrectly compute the truck's combined
weight in a statement like tt(vp->weight()). The reason for this should now be
clear: tt(vp) calls tt(Vehicle::weight()) and not tt(Truck::weight()), even
though tt(vp) actually points to a tt(Truck).

Fortunately, a remedy is available. In bf(C++) a tt(Vehicle *vp) may call a
function tt(Truck::weight()) when the pointer actually points to a tt(Truck).

The terminology for this feature is emi(polymorphism): it is as though the
pointer tt(vp) changes its type from a base class pointer to a pointer to the
class of the object it actually points to.  So, tt(vp) might behave like a
tt(Truck *) when pointing to a tt(Truck), and like an tt(Auto *) when pointing
to an tt(Auto) etc..+footnote(In one of the StarTrek movies, Capt.  Kirk was
in trouble, as usual. He met an extremely beautiful lady who, however,
later on changed into a hideous troll. Kirk was quite surprised, but the lady
told him: ``Didn't you know I am a polymorph?'')

Polymorphism is implemented by a feature called emi(late binding). It's called
that way because the decision em(which) function to call (a base class
function or a function of a derived class) cannot be made emi(compile-time),
but is postponed until the program is actually executed: only then it is
determined which member function will actually be called.

Note that in bf(C++) late binding is em(not) the default way functions are
called. By default emi(static binding) (or emi(early binding)) is used: the
class types of objects, object pointers or object refences determine which
member functions are called. Late binding is an inherently different (and
somewhat slower) procdure since it is decided i(run-time), rather than
i(compile-time) what function is called (see section ref(howpolymorphism) for
details). As bf(C++) supports em(both) late- and early-binding bf(C++)
programmers are offered an option in what kind of binding to use, and so
choices can be optimized to the situations at hand. Many other languages
offering object oriented facilities (e.g., bf(Java)) only offer late
binding. bf(C++) programmers should be keenly aware of this, as expecting
early binding and getting late binding might easily produce nasty bugs.

Let's have a look at a simple example (put here even though polymorphism
hasn't been covered yet at this point in order to have the example stand
clearly) to hone our awareness of the differences between early and late
binding. The example merely illustrates. Explanations of em(why) things are as
shown are found in subsequent sections of this chapter.

The following (using in-class implementations to reduce its size) shows a
little program that may be compiled and run:
        verbinclude(polymorphism/examples/notvirtual.cc)
    This program could have been constructed from some predecessor, in which
maybe just one class was defined. At some point its author decided that it
would have been nice to have a separate tt(Base) class, maybe in order to
factor out common functionality to be used by various derived classes. Note,
for example, how the tt(derived) object calls tt(process()) which is defined
in tt(Base): that's common functionality. Unfortunately, an error has crept
in. The aim (which is automatically realized in many other object oriented
programming languages) was that specialized functionality would be made
available in derived classes. So, tt(Derived) re-implements tt(hello()). Alas:
when run, the program displays tt(base hello). What went wrong? The answer is:
static binding. Due to static binding tt(process()) only knows about
tt(Base::hello()), and so that function is called. Polymorphism, which is not
the default in bf(C++), solves the problem and allows the author of the
classes to reach its goal. For the curious reader: prefix tt(void hello()) in
the tt(Base) class with the keyword tt(virtual) and recompile. Running the
modified program produces the intended and expected tt(derived hello). Why
this happens is explained next.