[Prev][Next][Index]
alternative to "trashed" (a real message this time)
Dave,
Apologies to you and everyone for sending your message again. I typed
the wrong characters...
> I agree that the constructor can't assume the invariant. This argues
> that perhaps some special syntax should be used so that users
> don't get confused.
I'm not sure I understand; I thought that C++ constructors do have a
fairly special syntax.
I meant special specification syntax.
Thanks for the example and reference to show you can invoke a destructor
and operator delete explictily. This argues that one should just specify
the "filling in" on the part of the constructor, and the "anti-filling in"
of the destructor.
There is a potential complicated difficulty here, as destructors are
called on objects with virtual functions and base classes, the vptr
must "swing" to that of the base class before executing the base
class' destructor. Is the destructor required to restore it to its
original value, to make the second invocation work correctly? The ARM
is fairly silent on this point. This question is relevant to
something we're working on, too; I may query comp.std.c++ and see if
any real language lawyers care to respond.
That seems like an implementation issue to me. Clearly it should be
restored correctly... one would hope that eventually implementations
would do that.
>> Symmetrically, destructors are distinguished member functions that get
>> to assume the class invariant on entry, but aren't required to
>> maintain them on exit. Thus, if a class has a non-trivial invariant,
>> it is necessarily illegal to call a member function on an object after
>> its destructor is called; you can't establish the pre-condition that
>> the invariant holds!
> But it's even illegal to call a member function if the invariant is trivial
> (:-).
I'm not sure I get your meaning; are you saying that it's illegal to
call a member function on an object after its destructor has been
called, even if the object has a trivial invariant the the destructor
didn't invalidate?
No, I was saying that it's just plain wrong to call a member function after
the destructor is called. I can't imagine that any sensible verification
logic would allow you to do anything on an object that has had its destructor
called. My point was that the triviality of the class's invariant has
nothing to do with it.
You could just define things this way; I was
proposing a rationale for what distinguishes the destructor from other
member functions. If we accept that calling a destructor without
doing a delete, via "p->~T()", is legal, then there is especially
good reason to treat the destructor differently from any other member
function. Usually, it will happen to be different from other member
functions in that it doesn't preserve the class invariant, in which
case no member function can be called on an object after the
destructor has. But this is not always necessary, as in the case
we're discussing.
I agree that one can call the destructor without calling delete.
I'm not sure I want to be able to specify that... Is there any good reason
for doing it?
> The only thing that worries me still is that one can construct an
> object (on the run-time stack) without using new,
> and destroy an object implictly by leaving a block.
> { Stack x;
> ...
> }
This seems to me exactly the reason for *not* mixing up the
specification of the storage state with the effects of constructors
and destructors; they really are complete separate in C++. If I write
a class T, there is *no way* I can keep a client from allocating
instances of T on both the stack in the heap. The semantic meaning of
"new T" *really is* the composition of a call to "::operator new"
followed by a constructor, and "delete p" really is a destructor
invocation followed by "::operator delete". So I persist in thinking
that the specification of "operator new" and "operator delete" is
where you should deal with FRESH and TRASHED.
Think of it this way: C++ really isn't that different from C. In C,
you would call "malloc", and then, by convention, call an initialization
routine to establish an invariant. Similarly, you might call a
destruction routine followed by "free". C++ just codifies these
conventions.
I guess I have to agree with this. This is the position I started with.
But it does seem strange with respect to the other specs in a class.
Is the stuff you refer to as the "malloc plumber" a paper on specifying
storage allocation?
Gary
Reference(s):