Author : Ian Joyner
Page : << Previous 9 Next >>
layout, the eye must trace a course
around the type specifications. This is a tiring activity. The eye has
a greater chance of missing the sought identifier, and the programmer
must resort to using the search function of a text editor to help out.
Other languages place the entity names first. For example:
class C
{
a ();
b ();
c () int;
d ();
e () char;
f () virtual void;
}
To those used to the ALGOL and FORTRAN style of type first, this
seems backwards. But name first is logical as a real world example
illustrates. Imagine if a dictionary is published, and the keywords are
not placed first, but rather the entry order is -
noun /obvrzen/ obversion, the act or result of obverting
Such a dictionary would not sell many copies, unless the marketers
managed to fool many people that the explanation of the meaning was more
correct because the order of layout was mysteriously magical. This
example illustrates how important subtle syntax decisions are, and why
PASCAL style languages might have ordered things contrary to FORTRAN,
ALGOL and others. The language designer must consider these trivial but
important alternatives. The layout of programming entities is essential
for effective communication. The dual roles of language syntax, and
programming style affect comprehension. A dictionary or index style
layout suggests placing entity names first, followed by their
definition.
3.24. Garbage Collection
One of the hallmarks of high level languages is that programmers
declare data without regard to how the data is allocated in memory. In
block structured languages, local variables are allocated on the stack,
and automatically deallocated when the block exits. This relieves the
programmer of a great burden. Garbage collection provides equivalent
relief in languages with dynamic entity allocation.
In C++ the programmer must manually manage storage due to the lack of
garbage collection. This is a difficult bookkeeping task that leads to
two opposite problems. Firstly, an object can be deallocated
prematurely, while valid references still exist (dangling pointers).
Secondly, dead objects might not be deallocated leading to memory
filling up with dead objects (memory leaks). Attempts to correct either
problem can lead to overcompensation and the other problem occurring. A
correct system is a fine balance. This is illustrated in the figure
below.
Dangling Correct Memory
pointers <---------> system <------------> leaks
These problems contribute to the fragility of C++ programs, and
usually result in system failure. Garbage-collection solves both
problems. Garbage-collection has an undeserved bad reputation due to
some early garbage-collectors having performance problems, instead of
working transparently in the background, as they can and should. These
problems are often over-emphasised as a justification for C++ ignoring
garbage collection. A possible solution is to build garbage collection
into the run time architecture, but allow the programmer to activate and
deactivate it manually. Garbage collection can be disabled in systems
where it is inappropriate.
In C++ it might be argued that the lack of garbage-collection is not
an engineering compromise. Its inclusion is nearly an engineering
impossibility, as a programmer can undermine the structures required for
implementing correctly working garbage-collection. While
garbage-collection might not actually be an impossibility in C++ (EC++),
it is difficult, and programmers would have to settle for a more
restricted way of programming. This could be a good thing. But then
the compromise to remain compatible with C becomes difficult, if the
compiler is to detect practices inconsistent with the operation of
garbage-collection.
3.25. Type-safe linkage
The C++ ARM explains that type-safe linkage is not 100% type safe.
If it is not 100% type-safe, then it is unsafe. It is the subtle errors
that cause the most problems, not the simple or obvious ones. Often
such errors remain undetected in the system until critical moments. The
seriousness of this situation cannot be underestimated. Many forms of
transport, such as planes, and space programs depend on software to
provide safety in their operation. The financial survival of
organisations can also depend on software. To accept such unsafe
situations is at best irresponsible.
The C++ ARM summarises the situation as follows - "Handling all
inconsistencies - thus making a C++ implementation 100% type-safe -
would require either linker support or a mechanism (an environment)
allowing the compiler access to information from separate compilations."
So why does the C++ compiler (at least AT&T's) not provide for
accessing information from separate compilations? Why is there not a
specialised linker for C++, that actually provides 100% type safety?
There is no reason why C++ should not be implemented this way. Building
systems out of preexisting elements is the common Unix style of software
production. This implements a form of reusability, but not in the truly
flexible manner of object-oriented reusability.
In the future, Unix could be replaced by object-oriented operating
systems, that are indeed 'open' to be tailored to best suit the purpose
at hand. By the use of pipes and flags, Unix software elements can be
reused to provide functionality that approximates what is desired. This
approach is valid and works with efficacy in some instances, like small
in-house applications, or perhaps for research prototyping, but is
unacceptable for widespread and expensive software, or safety critical
applications. In the last ten years the advantages of integrated
software have been acknowledged. Classic Unix systems don't provide
those advantages. Integrated systems are more ambitious, and place more
demands on developers. But this is the sort of software now being
demanded by end users. Systems that are cobbled together are
unacceptable.
A further problem with linking is that different compilation and
linking systems should use different name encoding schemes. This
problem is related to type-safe linkage, but is covered in the section
on 'reusability and compatibility'.
3.26. C++ and the software lifecycle
The software lifecycle has attracted a great deal of attention. It
is at least generally accepted that the activities in the lifecycle are
analysis of requirements, design, implementation, testing and error
correction, extension. Unfortunately, the result of identifying these
activities has resulted in a school of thought that the boundaries
between these activities are fixed, and that they should be
systematically separate, each being completed before the next is
commenced. It is often argued that if they are not cleanly separated,
then you are not practicing disciplined system development.
This view is incorrect. Someone who writes a program straight away
is actually doing all the steps in parallel. It might not be the best
way do do things in many circumstances, might or might not suit the
style and thinking of different people, but this works in some
scenarios, and can be the methodology of choice of disciplined thinkers.
Some people can hold a whole problem and solution in their head and work
in a disciplined fashion until the solution is complete. Mozart is said
to have composed this way, producing his last three symphonies in as
many months in 1788. Beethoven toiled far more over the production of
his works, taking years to complete one symphony. Both composers
produced masterpieces. Mozart wrote music directly, whereas Beethoven
wrote themes and ideas in his famous sketchbooks. The production of
masterpieces depends on skill, not on methodologies.
It is becoming accepted that the software lifecycle should be an
integrated process. Analysis, design and implementation should be a
seamless continuum.The activities of the lifecycle should progress in
parallel to expedite software development. Facts found out only as late
as the implementation stage can be fed back into the analysis and design
stages. The object-oriented approach supports this process. Artificial
separation of the steps leads to a large semantic gap between the steps.
The transformations required to bridge such semantic gaps are prone to
misinterpretation, time consuming and costly.
The same people should be responsible for all stages. This way they
take responsibility for the system as a whole, rather than passing the
buck and blame which occurs when analysts, designers and implementers
are different groups. This is not a popular viewpoint in traditional
hierarchical management structures where programmers get promoted to
designers who get promoted to analysts. Hierarchical management also
discourages people from feeling responsible for a product. This culture
must radically change if we are to produce quality systems.
We should have learnt from the extremes SA/SD. Some quarters
believed that methodology was all important, while programming and
programming languages were unimportant. Arcane and machine-oriented
programming languages strengthened this attitude. These languages
concentrate on the 'how' of computation, whereas the modellers correctly
demand notations that express the 'what', in order to be implementation
independent. A modern software language supports the integration of the
activities of design and implementation by being readable, and
problem-oriented. A language should be as close to design as possible.
The needs and
Page : << Previous 9 Next >>