In the following we will briefly explore some extensions to the basic object model, and try to establish why they may be considered useful. See slide 2-extensions.
Meta classes are a feature of Smalltalk supporting the definition of class-wide variables and so-called class methods. Class methods must be used to create new instances of the class. Neither Eiffel nor C++ support meta classes, although C++ supports so-called static data and function members, which are accessible by all instances of a class. The action of creating objects (as instances of classes) is for both languages taken care of by special creation functions or constructors. A feature that is felt to be lacking in C++ is the possibility of obtaining type information during runtime. Although libraries exist that provide the required functionality (to some extent), it seems likely that the language will be extended by constructs supporting this directly. (Currently, the ANSI standardization committee for C++ is studying proposals to introduce features supporting dynamic type information in C++.) See also section OMG. In most object-oriented systems, references to objects are system dependent (addresses in some memory space) and are usually lost when program execution stops. To facilitate debugging it would be convenient to be able to deal with (unique) symbolic references to objects. A more flexible scheme of object naming may also be important for distributed systems, since symbolic identities may also have meaning outside the program's execution space. See section distribution.
Object identity also plays an important role in object-oriented data bases, since the persistent storage of objects requires a naming scheme that allows objects to be written to stable storage and retrieved independently from the current execution.
To my mind, one of the most interesting extensions to the basic object model is the notion of active objects, objects with autonomous behavior. Important issues that relate to this extension are, what concurrency model must be supported and what to do with distribution. See chapter \ref{Distribution and concurrency}. Moreover, from the perspective of design we must ask, are active objects useful and how do they fit in with the design of sequential systems?
class Matherr { } class Overflow : public Matherr {} try { f();do some arithmetic
} catch (Overflow) {// handle Overflow
} catch (Matherr) {// handle non-Overflow Matherr
}
template< class T, class X > inline void Assert(T expr, X x) { if (!NDEBUG) if (!expr) throw x; }
Interventions are familiar from daily computing practice in the form of interrupts, such as are used to kill a process. Under Unix, an interrupt is implemented as a signal that may be associated with a handler. The interrupt handler usually invokes the exit function.
Exceptions, on the other hand, are not (yet) familiar to most programmers and are only supported by a few languages.
One of the languages supporting exceptions is Eiffel.
In Eiffel, exceptions are closely linked with the
notion of contracts.
Abnormal events occur whenever a contract is violated,
that is when either a pre-condition, post-condition
or invariant is not satisfied.
For each of these cases an exception handler may be specified
in the class defining the object for which an exception is raised.
The way in which abnormal events are dealt with in Eiffel
conforms to what is called the resumption model
in
Exceptions are part of the language definition of C++, but not many of the available compilers provide support for exceptions. The declaration of exceptions conforms to the definition of classes. For example, slide 6-abnormal declares a Matherr and an Overflow exception, which is publicly derived from Matherr. Obviously, the advantage of employing the class mechanism is that inheritance may be used to organize exceptions in groups. The most specific exception may then be tested for first, as in the catch statements following the try block in slide 6-abnormal. As the example shows, exception handling is activated by wrapping a group of statements or function calls in a try block. Whenever a failure occurs, the exception handlers declared by the subsequent catch statements will be tried to do something about it.
An exception may be raised by means of a throw statement as illustrated for the Assert function. The Assert function mimics the behavior of the assert macro, but in addition allows us to raise an explicit exception such as BadArgs or Invariant (provided these are defined as exceptions).
In contrast to Eiffel,
which allows for resuming the computation
after the occurrence of an exception,
C++ supports only termination semantics
for exceptions, which results in terminating the function call
that caused the exception.
The main reason to adopt termination semantics
is (according to
Lacking adequate language support, both exceptions and interventions may be simulated, respectively by setting a global flag which is tested after the operation and by adding intervention routine parameters to function definitions. However, such simulations are tedious and error-prone to implement and, moreover, violate encapsulation and endanger extensibility.