NOTE: Except where otherwise indicated, the comments in this section apply to all OOM-2 objects, whether members of traced classes or of untraced classes.
As might be expected, two object references that are members of the same class are assignment compatible. (It turns out that there is somewhat more to object assignment compatibility than this, however; see section 19.7.) Thus, if one has:
VAR obj1, obj2 : ClassyClass;
it is perfectly legitimate in the course of a program to write
obj1 := obj2;
as long as the programmer understands that this is a copying of references, not of the values accessed by those references. Thus, there is little point in such an assignment being made before the second of the two objects referred to has even been created, as the call to CREATE will change the reference value.
Likewise, two objects can be compared in expressions such as those found in
IF obj1 = obj 2 WHILE obj1 # obj2
and again, it is the references that are being compared, not the values accessed by those references. Of course, if two references are to the same memory, the values stored there are indeed the same, but if two different but compatible objects do happen to have the same values in their attribute components, this kind of code will never discover that fact.
Earlier, when dealing with pointers, there was a useful value (NIL) that could be used for initialization. A similar value is used for initialization of object references.
The pervasive value EMPTY is assignment compatible to all object references.
It will, of course, cause an exception to be raised if one attempts to use a component of an object that has the value EMPTY, just as it would to attempt to dereference the pointer value NIL. (The object in question is the empty object; it currently has no components.)
In yet another parallel with pointers, should the attempt to call CREATE on an object fail to allocate memory for the object, its reference will have the value EMPTY after the failure, but no exception will be raised. Bullet-proof code ought to have a check for success after a CREATE:
CREATE (myObject); IF myObject # EMPTY THEN (* carry on *)
For traced objects only, there is an automatic initialization to the value EMPTY on declaration. This includes traced objects declared in static modules, in procedures, and even in dynamic modules inside procedures. This will help prevent references to objects not yet created. Setting the value of a traced object reference to EMPTY is also a way of informing the garbage collector that the memory it previously held may be collected (as least, insofar as this reference is concerned.)
On the other hand, if a reference to an object of a traced class that currently has the value EMPTY is assigned some other value, the garbage collector is informed of that fact as well.
In addition, any references to objects of traced classes declared in procedures (including parameters) are reported to the garbage collector as empty (that is, defunct) when the procedure is exited (whether normally or exceptionally). This is also true of any traced object references that may have been used to construct components of an untraced object that is subsequently destroyed.
For references to objects of untraced classes, the standard does not say what the value of the reference is after a call to DESTROY. It has to be assumed, therefore, that the value is undefined at that point, not that it is necessarily EMPTY.
Although pointers and object references behave in similar ways, they are different kinds of entities. This should be evident if for no other reason than that pointers are dereferenced using the dereferencing operator ^, wheras the components of an object are accessed using a qualified identifier. These differences mean, among other things, that there is no assignment compatibility between object references and pointers.