[net.lang.c++] destructors and free-storage management

solomon@crystal.UUCP (Marvin Solomon) (04/03/86)

At the top of page 165 of the C++ book (section 5.5.7), we find the
following sentences:

	There is no equivalent feature enabling a destructor to decide
	if its object was created using 'new', nor is there a feature
	enabling it to decide whether it was invoked by 'delete' or
	by an object going out of scope.  If knowing this is important,
	the user can store the relevant information somewhere for the
	destructor to read.

Well, I guess this is not a bug but a feature, since it's so clearly
documented :-), but it seems a shame, especially since the information
is provided to the destructor, but the C++ programmer can't get at it.

In the 1.0 implementation, if I declare

	struct node{
		int val;
		node *next;
		~node();
	};
	node::~node(){
		// something
	}

the compiler generates the following code:

	int _node__dtor( _auto_this, _auto__free )
	register struct node *_auto_this ;
	int _auto__free;
	{
		/* something */
		if (_auto_this) if(_auto__free) _delete((int *)_auto_this) ;
	} ;

Note the second argument to _node__dtor().  Each occurence of
"delete p", where p is of type (node *), translates to
	_node__dtor((struct node *)p, 1);
whereas, at the end of a scope with an automatic variable f, the
compiler generates
	_node__dtor((struct node *)&f, (int) 0);
Thus the compiler tells the default destructor whether it was invoked by
'delete', but the information is hidden from the C++ programmer.
That hardly seems sporting!

The paragraph goes on to say

	Alternatively, the user can ensure objects of that class are only
	allocated appropriately.  If the former problem is handled, the latter
	is uninteresting.

In other words, if I never declare any variables of type 'node', but only
'node *', the destructor can assume every object is allocated in free
store (and thus can safely link it onto a free list, or whatever).
Allow me to suggest an alternative (and I claim much more reasonable)
discipline:  Never use the 'unary &' operator.  Then the latter
problem becomes much more interesting.  The destructor can assume that
if it was called by 'delete', it should save 'this' on a free list,
but if it was called by a variable going out of scope, it doesn't
have to do anything.

I can't take credit for this idea--I stole it directly from Pascal.
In Pascal, there are two disjoint classes of variable:  automatic
variables, which can be named but pointed to, and "heap" variables,
allocated by new() and freed by dispose().  The former are automatically
reclaimed by the runtime when they go out of scope, whereas the
latter must be explicitly deleted by the programmer.  This scheme
suffers (as does free storage management in C++) from the dangers of
uncollected garbage and dangling references, but at least it doesn't 
allow the stack to become corrupted by an attempt to dispose() an
automatic variable.

How about it Bjarne?  Why not let me write

	node::~node() {
		if (free) {
			next = free_head;
			free_head = this;
		}
	}
	node::node() {
		if (this==0)
			if (free_head) {
				this = free_head;
				free_head = free_head -> next;
			}
			else this = (node *) new char(sizeof(node));
	}

The only argument I can see against this extension is that it
introduces another reserved word (at least inside destructors).
-- 
	Marvin Solomon
	Computer Sciences Department
	University of Wisconsin, Madison WI
	solomon@uwisc
	...{ihnp4,seismo,allegra}!uwvax!solomon