Frank Heckenbach wrote:
... snip ...
What about the following situation:
Create an object Foo (store pointer) and an object Bar that points to Foo (i.e., contains the store pointer).
Create an object Baz that also points to Foo (reference pointer).
Destroy Bar. IIUIC, Foo will be disposed of and the reference pointer in Baz invalidated. That's nice so I get an error message rather than a crash when I try to access Baz.Foo, but I'd prefer to actually access it. ;-)
I guess I must be missing something, since that's only a very simple example of what happens all the time in GCC/GPC.
Besides, when you really want to evaluate things, you have to describe all the hidden costs first. AFAICS, for every object you need an implicit list containing all the reference pointers, so they can be invalidated later.
When copying a pointer, you have to add the destination to the list, and remove its previous value (if any) from its list. If not done properly, this can easily be O(n) which is unacceptable. So you need at least a doubly-linked list or something like this.
These are memory and runtime issues, and given that copying pointers is quite common in GPC, these effects must be analyzed precisely.
In Pascal, but not in C, you can force all pointer copying to be done via a subroutine, because there is an essential difference between pointers and VAR parameters. That subroutine will increase the reference count in the actual heap storage area by 1. At the same time you have to make all destruction, including destruction via procedure return and loss of local variables, decrement that reference count. If dispose finds a count greater than 1 it decrements and flags the storage as invalid, so that use by an orphan pointer will bomb. This means that access has also got to be via a routine to check that invalid flag. When anything decrements the ref to 0, the storage is released.
This complicates pointer copying and passing, but leaves the actual derefencing untouched. Note that a WITH statement effectively creates a pointer copy.
Another technique ties in with run-time checks. Any pointer use is validated by a routine, which effectively walks the list of allocated storage. If not found, the use is invalid and the system crashes. This again adds considerable overhead. It was kept disabled in PascalP because of that.
Whatever you do, from checking to ignoring, Pascal should not have garbage collection. It does not have the wild pointer usage of C (and C++), and so can be controlled. GC brings in things like unexpected pauses which are fatal to any real-time or quasi-real-time use. If you are going to create the overhead for GC then you can better create the overhead for proper run-time checking.