Topic : A Critique of C++
Author : Ian Joyner
Page : << Previous 12  Next >>
Go to page :


to program.  It matches very effectively the way we think.  If C++
has been designed according to the Sapir-Whorf hypothesis, its
philosophical basis does not serve a computer industry that should shape
tools best suited to its purposes, processes, thinking, and concepts.

  

5.  On Writing


   During the development of this critique, I realised it had grown
larger than I had intended, and that my writing style needed some polish
for such a large work.  During my research, some colleagues recommended
a small book, "The Elements of Style," [S & W 79].  This has been around
for most of this century in one form or another.  William Strunk, the
original author had some stern advice for his students:

   "Vigorous writing is concise.  A sentence should contain no
unnecessary words, a paragraph no unnecessary sentences, for the same
reason that a drawing should have no unnecessary lines and a machine no
unnecessary parts."

   The machines that the software professional develops are not built,
but written.  Strunk's last sentence prompts consideration of the
relationship of writing to software development.  A common situation is
taking several thousand lines of incomprehensible 'code', and making it
execute efficiently.  After spending considerable time we often realise
what the program does, and reduce it to several hundred lines of program
'text' that runs ten times faster.  Strunk's quote should be applied to
programming.  A routine should contain no unnecessary declarations or
instructions, a system no unnecessary routines.  It can also be applied
to programming languages.  A programming language should contain no
unnecessary constructs.  This is the root of my dissatisfaction with
C++.  Much of it is unnecessary, even for the most complex systems.  Its
syntax is ugly.  C++ has become what the C world has constantly
criticised in languages like PL/1 and Ada.  Only C++ is worse.  We need
to regain artistic elegance and simplicity.

  

6.  Generic C criticisms


   These criticisms apply to the C base language, but in general
adversely affect C++.  R.P.Mody [Mody 91] gives an excellent general
criticism of C.  He says that to properly understand C you must
understand the insides of the compiler.  He gives many examples of how C
obscures rather than clarifies software engineering.  He concludes that
he is "appalled at the monstrous messes that computer scientists can
produce under the name of 'improvements'.  It is to efforts such as C++
that I here refer.  These artifacts are filled with frills and features
but lack coherence, simplicity, understandability and implementability.
If computer scientists could see that art is at the root of the best
science, such ugly creatures could never take birth."

   C's popularity is based on several myths.  Firstly, that it is a high
level language.  It is not.  It is a structured assembler oriented
towards the low level machine domain, not to the problem domain of a
high level language.  Secondly, that it is small and simple.  Its
semantics are not simple, but it is very simple to make catastrophic
errors.  Thirdly, that it is portable.  Certainly compilers are
available on many platforms, but this does not make programs portable,
especially to diverse, and future architectures.  Platform independence
achieves portability.  Fourthly, that it is efficient.  What seems
efficient on some platforms is the very antithesis of efficiency on
other platforms.  It seems efficient on certain platforms because it
allows the lower level machine-oriented architecture to be visible at a
higher level, instead of being handled transparently by the compiler.
This means that programs will be locked into certain styles of
architecture, or into current styles of technology, instead of
protecting program investment against future technological change.  And
lastly, that the semantics are mathematically rigorous.  Anyone who
reads the C++ ARM will realise just how poorly defined the language is.
Anyone who has practiced C will know how many traps there are to fall
into.

  

6.1.  Pointers

   C pointers are a low level mechanism that should not be the concern
of programmers.  Pointers mean the programmer must manipulate low level
address mechanisms, and be concerned with lvalue and rvalue semantics,
which are machine oriented and not problem oriented as you would expect
of a high level language.  A compiler can easily handle such issues
without loss of generality or efficiency.  Memory models of different
environments often affect the definition of pointers.  Memory model
details such as near and far pointers should be transparent to the
programmer.

   The programmer must also be concerned with correct dereferencing of
pointers to access referenced entities.  Use of pointers to emulate by
reference function parameters are an example.  The programmer has to
worry about the correct use of &s and *s.  (See the section on function
parameters.)

   Pointer arithmetic is error prone.  Pointers can be incremented past
the end of the entities they reference, with subsequent updates possibly
corrupting other entities.  How many lurking and undetected errors are
in programs because of this? This illustrates how C undermines OOP by
providing a mechanism where state outside an object's boundaries can be
changed.  Since pointers are intrinsic to writing software in C this
exacerbates the problem.  Pointers as implemented in C make the
introduction of advanced concepts like garbage collection and
concurrency difficult.

   Another consideration is that dynamic memory implementations vary
between platforms.  Some environments make memory block relocation
easier by having all pointers reference objects via a master pointer
which contains the actual address of the block.  The location of the
master pointer never changes, so relocation of the block is hidden from
all pointers that reference it.  When the block is relocated, only the
master pointer needs to be updated.

   On the Macintosh, for example, the double indirection mechanism of
'handles' facilitates relocation of objects.  Object Pascal makes these
handles transparent to the programmer.  This is similar to the Unisys A
Series approach where object 'descriptors' access the target object via
a master descriptor that stores the actual address of the object.  On
the A Series this is transparent to programmers in all languages, as
this transparency is realised at a level lower than languages.  The A
series descriptor mechanism also provides hardware safety checks that
mean that pointers cannot overrun, and arrays cannot be indexed out of
bounds.  C cannot be implemented particularly well on such machines, as
C's mechanisms are lower level than the target environment.

   Other environments do not provide object relocation, so double
indirection is an unnecessary overhead.  In order for programs to be
portable and to be at their most efficient in different target
environments, such system details should be the concern of the target
compilation system, not of the programmer.

   C's pointer declaration syntax causes another small problem:

   int* i, j;

   This does not mean, as might be easily read -

   int *i, *j;

   but

   int *i, j;

   and should be written thus to avoid confusion.

  

6.2.  Arrays

   Page 137 of the C++ ARM notes that C arrays are low level, yet not
very general, and unsafe.  Page 212 admits, "the C array concept is weak
and beyond repair." Modern software production is far less dependent on
arrays than in the past, especially in the object-oriented environment.
The trade off to be optimal, rather than general and safe no longer
applies for most applications.  C arrays provide no run-time bounds
checking, not even in test versions of software.  This compromises
safety and undermines the semantics of an array declaration, ie an array
is declared to be a particular size, and can only be indexed by values
within the given bounds.  An index to an array is a parameter in the
domain of the array function.  An index out of bounds is not a member of
the domain, and should be treated as severely as divide by zero.  C has
no notion of dynamically allocated arrays, whose bounds are determined
at run time, as in ALGOL 60.  This limits the flexibility of arrays.
The C definition of arrays compromises both safety and flexibility.

   One view of arrays is just another object-oriented entity which
should be treated in an object-oriented manner as a class of data
structure.  It should have interface definitions, and consistency checks
inherent in object-oriented systems.  Another view is that an array is
an implementation of a function, where pairs of values explicitly map
the domain to the range, rather than being computed.  This suggests that
Algol was incorrect in distinguishing arrays by using square brackets.
An array just maps the input argument (the index) to a value of the type
of the array.  An array can be viewed as a random access stack.

   [Ince 92] considers that arrays and pointers need not be relied upon
so heavily in modern software production, as higher level abstractions
such as sets, sequences, etc are better suited to the problem domain.
Arrays, and pointers can be provided in an object- oriented framework,
and used as low level implementation techniques for the higher level
data abstractions.  As has

Page : << Previous 12  Next >>