The languages Smalltalk, Eiffel and C++
may be regarded as the three most important
(and popular) representatives of classical
object-oriented languages, classical in the sense
of being based on a class/object distinction.
Criteria for comparison
- class libraries
- programming environment
- language characteristics
slide: Criteria for comparison
In this section we will compare these languages
with respect to what may be called their intrinsic
language characteristics.
Before that, however, we will indicate
some other (more external) criteria for comparison
such as the availability of class libraries and
the existence of a programming environment.
See slide [5-comparison].
Our discussion is based on (but in some respects disagrees
with) [BPS89].
Criteria for comparison
When choosing a particular programming language
as a vehicle for program development a number
of factors play a role, among which are the availability
of a class library, the existence of a good programming
environment, and, naturally,
the characteristics of the language itself.
Class libraries
An important criterion when selecting a class library
may be the availability of sufficient class library support.
A general class library, and preferably libraries
suitable for the application domain one is working in,
may drastically reduce development time.
Another important benefit of using (well-tested)
class libraries is an improvement of the reliability of the
application.
Smalltalk (that is Smalltalk-80 of ParcPlace Systems)
comes with a large collection of general purpose and graphics
programming classes, that are identical for both MS-DOS and
Unix platforms.
Also Eiffel (albeit only on Unix platforms)
comes with a standard collection of well-documented
libraries containing common data structures, container
classes and classes for graphics programming.
For both Smalltalk and Eiffel, the accompanying classes
may almost be considered to be part of the language
definition, in the sense that they provide a
standard means to solve particular problems.
In contrast, for C++ there is almost no standard
library support (except for IO stream classes).
Even worse, the various C++ compiler vendors
disagree considerably in what functionality the standard
class libraries of C++ must offer.
Fortunately, however, there is an increasingly large
number of third party libraries (commercially and non-commercially)
available.
These are more extensively described in section [libraries].
The burden of choosing the appropriate libraries
is, however, placed on the shoulders of a user
or a company,
which has the advantage that a more optimal
solution may be obtained
than possible within the confines of standard libraries.
Programming environments
Another selection criterion in choosing a language is
the existence of a good programming environment.
What constitutes a good programming environment
is not as simple as it may seem,
since that depends to a large extent upon the experience
and preferences of the user.
For example, with respect to operating systems,
many novice users favor a graphical interface as
originally offered by the Apple Macintosh computers,
while experienced users often feel constrained
by the limitations imposed by such systems.
In contrast, experienced users may delight in the
terseness and flexibility of the command-based
Unix operating system, which leads
to outright bewilderment with many novice users.
Of the object-oriented programming languages
we consider in this book,
Smalltalk definitely offers the most comprehensive
programming environment (including editors, browsers and
debuggers).
For Unix systems, Eiffel comes with a number of
additional tools (such as a graphical browser, and a
program documentation tool) to support program
development (and maintenance).
In contrast, C++ usually comes with nothing at all.
However, increasingly many tools (including browsers
and debuggers) have become available.
See section [pde].
Language characteristics
Despite the commonality between Smalltalk, Eiffel and C++
(which may be characterized by saying that they all support
data abstraction, inheritance, polymorphism
and dynamic binding), these languages widely
disagree on a number of other properties,
such as those listed in slide [5-characteristics].
Language characteristics
- uniformity of data structures
- documentation value
- reliability
- inheritance mechanisms
- efficiency
- memory management
- language complexity
slide: Language characteristics
These characteristics were used in [BPS89] to compare
Smalltalk, Eiffel and C++ with the language Oberon,
which offers what may be called a minimal (typed)
object-oriented language.
We will, however, discuss Oberon in section [dimensions],
and limit our discussion here to Smalltalk, Eiffel and C++.
Language characteristics
Smalltalk, Eiffel
and C++ differ with respect to
a number of language characteristics.
An indication of the differences between these
languages is given slide [5-language-characteristics].
slide: Comparing Smalltalk, Eiffel and C++
This characterization conforms to the one given in
[BPS89], with which I think the majority of the
object-oriented community will agree.
It is further motivated below.
However, the places indicated by an asterisk
deserve some discussion.
In particular, I wish to stress that I disagree with
characterizing the reliability of C++ as low.
(See below.)
Uniformity
In Smalltalk, each data type is described by a class.
This includes booleans, integers, real numbers and control
constructs.
In Eiffel there is a distinction between elementary
data types (such as boolean, integer and real)
and (user-defined) classes.
However (in the later versions of Eiffel)
the built-in elementary types behave as if declared
by pre-declared classes.
For C++, the elementary data types and simple data structures
(as may be defined in C) do not behave as objects.
To a certain extent, however,
programmers may deal with this non-uniformity by some
work-around, for example by overloading functions
and operators or by embedding built-in types in a (wrapper) class.
Documentation value
Smalltalk promotes a consistent style in writing programs,
due to the assumption that everything is an object.
One of perhaps the most important features
of Eiffel is the use of special keywords for
constructs to specify the correctness of programs
and the behavioral properties that determine the external
interface of objects.
Moreover, Eiffel provides a tool to extract a description
of the interface of the method classes
(including pre- and post-conditions associated with a method)
which may be used to document (a library of) classes.
To my taste, however, the Eiffel syntax leads to
somewhat verbose programs, at least in comparison
with programs written in C++.
The issue of producing documentation from C++ is still open.
A number of tools exist (including a WEB-like system
for C++ and a tool to produce manual pages from C++
header files, see section [pde]) but no standard has
yet emerged.
Moreover, some people truly dislike the terseness of C/C++.
Personally, I prefer the C/C++ syntax above the syntactical
conventions of both Eiffel and Smalltalk,
provided that it is used in a disciplined fashion.
Reliability
Smalltalk is a dynamically typed
language.
In other words, type checking, other than detecting runtime errors,
is completely absent.
Eiffel is generally regarded as a language possessing
all characteristics needed for writing reliable programs,
such as static type checking and constructs for stating
correctness assertions (that may be checked at runtime).
In contrast, due to its heritage from C, the
language C++ is still by many considered as unreliable.
In contrast to C, however, C++ does
provide full static type checking, including the signature
of functions and external object interfaces
as arise in independent compilation of module files.
Nevertheless, C++ only weakly supports type checking
across module boundaries.
In contrast to common
belief, Eiffel's type system is demonstrably inconsistent,
due to a feature that enables a user to dynamically define the
type of a newly created object in a virtual way
(see section [self-reference]).
This does not necessarily lead to type-insecure
programs though, since the Eiffel compiler
employs a special algorithm to detect such cases.
In contrast, the type system of C++ is consistent
and conforms to the notion of subtype as introduced
informally in the previous part.
Nevertheless, C++ allows
the programmer to escape the rigor of the
type system by employing casts.
An important feature of Eiffel
is that it supports assertions that may be validated
at runtime.
In combinations with exceptions, this provides
a powerful feature for the development
of reliable programs.
At the price of some additional coding (for example,
to save the current state to enable the use
of the old value), such
assertions may be expressed by using the assert macros
provided for C++.
A better way would probably be to employ
exceptions, but these are supported by only
a few C++ compilers.
In defense of C++,
it is important to acknowledge that only C++ offers
adequate protection mechanisms to shield classes
derived by inheritance from the implementation details
of their ancestor classes.
Neither Smalltalk nor Eiffel offer such protection.
Inheritance
Smalltalk offers only single inheritance.
In contrast, both Eiffel and C++ offer multiple inheritance.
For statically typed languages, compile-time
optimizations may be applied that result in only
a low overhead.
In principle, multiple inheritance allows one to model
particular aspects of the application domain in a flexible
and natural way.
See section [diamond] for a further discussion.
As far as the assertion mechanism offered by Eiffel
is concerned,
[Meyer88] offers clear guidelines prescribing how
to use assertions in derived classes.
However, the Eiffel compiler offers no assistance
in verifying whether these rules are followed.
The same guidelines apply to the use of assertions
in C++, naturally lacking compiler support as well.
Efficiency
Smalltalk, being an interpreted language, is typically
slower than conventionally compiled languages.
Nevertheless, as discussed in section
[self-implementation], interpreted object-based
languages allow for significant optimizations,
for example by employing runtime compilation techniques.
The compilation of Eiffel programs can result in programs
having adequate execution speed.
However, in Eiffel dynamic binding takes place in principle
for all methods.
Yet a clever compiler can significantly
reduce the number of indirections needed to execute a method.
In contrast to C++, in Eiffel all objects are created
on the heap.
The garbage collection needed to remove these objects
may affect the execution speed of programs.
As we have discussed in section [benefits],
C++ has been designed with efficiency in mind.
For instance, the availability of inline functions,
and the possibility to allocate objects on the runtime stack
(instead of on the heap),
allow the programmer to squeeze out the last drop
of efficiency.
However, as a drawback, when higher level functionality
is needed
(as in automatic garbage collection)
it must be explicitly programmed, and a similar price
as when the functionality would have
been provided by the system has to be paid.
The only difference is that the programmer has a choice.
Language complexity
Smalltalk may be regarded as having a low language complexity.
Control is primarily effected by message passing,
yet, many of the familiar conditional and iterative
control constructs reappear in Smalltalk programs
emulated by sending messages.
This certainly has some elegance, but does not necessarily
lead to easily comprehensible programs.
Eiffel contains few language elements
that extend beyond object-oriented programming.
In particular, Eiffel does not allow for overloading
method names (according to signature) within a class.
This may lead to unnecessarily elaborate method names.
(The new version of Eiffel (Eiffel-3) does
allow for overloading method names.)
Without doubt, C++ is generally regarded as a highly complex
language.
In particular, the rules governing the overloading of operators
and functions are quite complicated.
The confusion even extends to the various
compiler suppliers, which is one of the
reasons why C++ is still barely portable.
Somewhat unfortunately,
the rules for overloading and type conversion
for C++ have to a large extent been determined
by the need to remain compatible with C.
Even experienced programmers need occasionally
to experiment to find out what will happen.
According to [BPS89], C++ is too large and contains
too much of the syntax and semantics inherited from C.
However, their motto small is beautiful
is not so obvious as it seems.
The motivations underlying the introduction of
the various features incorporated in C++
are quite well explained in [St94].
The main problem, to my mind, in using
C++ (or any of the object-oriented languages for
that matter)
lies in the area of design.
We still have insufficient experience in using abstract
data types to define a complete method and operator interface
including its relation to other data types (that is its behavior
under the various operators and type conversions
that apply to a particular type).
The problem is hence not only one of language design but
of the design of abstract data types.