📄 tij0024.html
字号:
of the more complex topics in C++).
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">A
singly-rooted hierarchy makes it much easier to implement a garbage collector.
The necessary support can be installed in the base class, and the garbage
collector can thus send the appropriate messages to every object in the system.
Without a singly-rooted hierarchy and a system to manipulate an object via a
handle, it is difficult to implement a garbage collector.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Since
run-time type information is guaranteed to be in all objects, you’ll
never end up with an object whose type you cannot determine. This is especially
important with system level operations, such as exception handling, and to
allow greater flexibility in programming.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">You
may wonder why, if it’s so beneficial, a singly-rooted hierarchy
isn’t it in C++. It’s the old bugaboo of efficiency and control. A
singly-rooted hierarchy puts constraints on your program designs, and in
particular it was perceived to put constraints on the use of existing C code.
These constraints cause problems only in certain situations, but for maximum
flexibility there is no requirement for a singly-rooted hierarchy in C++. In
Java, which started from scratch and has no backward-compatibility issues with
any existing language, it was a logical choice to use the singly-rooted
hierarchy in common with most other object-oriented programming languages.
</FONT><a name="_Toc375545201"></a><a name="_Toc408018398"></a><P></DIV>
<A NAME="Heading32"></A><H3 ALIGN=LEFT>
Collection
libraries and support
<P>for
easy collection use
</H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Because
a collection is a tool that you’ll use frequently, it makes sense to have
a library of collections that are built in a reusable fashion, so you can take
one off the shelf and plug it into your program. Java provides such a library,
although it is fairly limited in Java 1.0 and 1.1 (the Java 1.2 collections
library, however, satisfies most needs).
</FONT><P></DIV>
<A NAME="Heading33"></A><H4 ALIGN=LEFT>
Downcasting
vs. templates/generics
</H4>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">To
make these collections reusable, they contain the one universal type in Java
that was previously mentioned:
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Object</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
The singly-rooted hierarchy means that everything is an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Object</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
so a collection that holds
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Object</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">s
can hold anything. This makes it easy to reuse.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">To
use such a collection, you simply add object handles to it, and later ask for
them back. But, since the collection holds only
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Object</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">s,
when you add your object handle into the collection it is upcast to
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Object</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
thus losing its identity. When you fetch it back, you get an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Object</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
handle, and not a handle to the type that you put in. So how do you turn it
back into something that has the useful interface of the object that you put
into the collection?
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Here,
the cast is used again, but this time you’re not casting
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>up</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
the inheritance hierarchy to a more general type, you cast
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>down</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
the hierarchy to a more specific type. This manner of casting is called
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>downcasting</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
With upcasting, you know, for example, that a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Circle</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is a type of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
so it’s safe to upcast, but you don’t know that an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Object</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is necessarily a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Circle</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
or a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
so it’s hardly safe to downcast unless you know that’s what
you’re dealing with.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">It’s
not completely dangerous, however, because if you downcast to the wrong thing
you’ll get a run-time error called an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>exception,</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
which will be described shortly. When you fetch object handles from a
collection, though, you must have some way to remember exactly what they are so
you can perform a proper downcast.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Downcasting
and the run-time checks require extra time for the running program, and extra
effort from the programmer. Wouldn’t it make sense to somehow create the
collection so that it knows the types that it holds, eliminating the need for
the downcast and possible mistake? The solution is
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>parameterized
types
</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
which are classes that the compiler can automatically customize to work with
particular types. For example, with a parameterized collection, the compiler
could customize that collection so that it would accept only
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">s
and fetch only
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Shape</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">s.</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Parameterized
types are an important part of C++, partly because C++ has no singly-rooted
hierarchy. In C++, the keyword that implements parameterized types is
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>template</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
Java currently has no parameterized types since it is possible for it to get by
– however awkwardly – using the singly-rooted hierarchy. At one
point the word
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>generic</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
(the keyword used by Ada for its templates) was on a list of keywords that were
“reserved for future implementation.” Some of these seemed to have
mysteriously slipped into a kind of “keyword Bermuda Triangle” and
it’s difficult to know what might eventually happen.
</FONT><a name="_Toc375545202"></a><a name="_Toc408018399"></a><P></DIV>
<A NAME="Heading34"></A><H3 ALIGN=LEFT>
The
housekeeping dilemma:
<P>who
should clean up?
</H3>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Each
object requires resources in order to exist, most notably memory. When an
object is no longer needed it must be cleaned up so that these resources are
released for reuse. In simple programming situations the question of how an
object is cleaned up doesn’t seem too challenging: you create the object,
use it for as long as it’s needed, and then it should be destroyed.
It’s not too hard, however, to encounter situations in which the
situation is more complex.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Suppose,
for example, you are designing a system to manage air traffic for an airport.
(The same model might also work for managing crates in a warehouse, or a video
rental system, or a kennel for boarding pets.) At first it seems simple: make a
collection to hold airplanes, then create a new airplane and place it in the
collection for each airplane that enters the air-traffic-control zone. For
cleanup, simply delete the appropriate airplane object when a plane leaves the
zone.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">But
perhaps you have some other system to record data about the planes; perhaps
data that doesn’t require such immediate attention as the main controller
function. Maybe it’s a record of the flight plans of all the small planes
that leave the airport. So you have a second collection of small planes, and
whenever you create a plane object you also put it in this collection if
it’s a small plane. Then some background process performs operations on
the objects in this collection during idle moments.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Now
the problem is more difficult: how can you possibly know when to destroy the
objects? When you’re done with the object, some other part of the system
might not be. This same problem can arise in a number of other situations, and
in programming systems (such as C++) in which you must explicitly delete an
object when you’re done with it this can become quite complex.
</FONT><A NAME="fnB6" HREF="#fn6">[6]</A><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">With
Java, the garbage collector is designed to take care of the problem of
releasing the memory (although this doesn’t include other aspects of
cleaning up an object). The garbage collector “knows” when an
object is no longer in use, and it then automatically releases the memory for
that object. This, combined with the fact that all objects are inherited from
the single root class
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Object</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
and that you can create objects only one way, on the heap, makes the process of
programming in Java much simpler than programming in C++. You have far fewer
decisions to make and hurdles to overcome.
</FONT><P></DIV>
<A NAME="Heading35"></A><H4 ALIGN=LEFT>
Garbage
collectors
<P>vs.
efficiency and flexibility
</H4>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">If
all this is such a good idea, why didn’t they do the same thing in C++?
Well of course there’s a price you pay for all this programming
convenience, and that price is run-time overhead. As mentioned before, in C++
you can create objects on the stack, and in this case they’re
automatically cleaned up (but you don’t have the flexibility of creating
as many as you want at run-time). Creating objects on the stack is the most
efficient way to allocate storage for objects and to free that storage.
Creating objects on the heap can be much more expensive. Always inheriting from
a base class and making all function calls polymorphic also exacts a small
toll. But the garbage collector is a particular problem because you never quite
know when it’s going to start up or how long it will take. This means
that there’s an inconsistency in the rate of execution of a Java program,
so you can’t use it in certain situations, such as when the rate of
execution of a program is uniformly critical. (These are generally called
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>real
time
</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">programs,
although not all real-time programming problems are this stringent.)
</FONT><A NAME="fnB7" HREF="#fn7">[7]</A><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
designers of the C++ language, trying to woo C programmers (and most
successfully, at that), did not want to add any features to the language that
would impact the speed or the use of C++ in any situation where C might be
used. This goal was realized, but at the price of greater complexity when
programming in C++. Java is simpler than C++, but the tradeoff is in efficiency
and sometimes applicability. For a significant portion of programming problems,
however, Java is often the superior choice.
</FONT><a name="_Toc375545203"></a><a name="_Toc408018400"></a><P></DIV>
<HR><DIV ALIGN=LEFT><A NAME="fn6" HREF="#fnB6">[6]</A><FONT FACE="Carmina Md BT" SIZE=2 COLOR="Black">
Note that this is true only for objects that are created on the heap, with
</FONT><FONT FACE="Carmina Md BT" SIZE=2 COLOR="Black"><B>new</B></FONT><FONT FACE="Carmina Md BT" SIZE=2 COLOR="Black">.
However, the problem described, and indeed any general programming problem,
requires objects to be created on the heap.
</FONT><P></DIV><DIV ALIGN=LEFT><A NAME="fn7" HREF="#fnB7">[7]</A><FONT FACE="Carmina Md BT" SIZE=2 COLOR="Black">
According to a technical reader for this book, one existing real-time Java
implementation (www.newmonics.com) has guarantees on garbage collector
performance.
</FONT><P></DIV>
<div align="right">
<a href="tij_c.html">Contents</a> | <a href="tij0023.html">Prev</a> | <a href="tij0025.html">Next</a>
</div>
</body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -