www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Newbie initial comments on D language - destructor usage

reply Edward Diener <eddielee_no_spam_here tropicsoft.com> writes:
I have a small nit having to do with a general comment which perhaps I
did not really understand. In the documentation on class destructors I read:

"When the garbage collector calls a destructor for an object of a class
that has members that are references to garbage collected objects, those
references are no longer valid. This means that destructors cannot
reference sub objects. This rule does not apply to auto objects or
objects deleted with the DeleteExpression."

This comment makes perfect sense up to the last sentence. Except for a
'scope' class a destructor can not possibly know how it is being called
so if someone can explain to me how a destructor can know when it is OK
to reference sub-objects or not, I would love to hear about it. I would
have thought that referencing sub-objects in a destructor was just
forbidden unless the class was a 'scope' class and had to reference a
sub-object to possibly release a non-memory resource. But perhaps I am
missing the full meaning of the comment and someone can explicate the
issues involved for me.
Jan 28 2008
next sibling parent Sean Kelly <sean f4.ca> writes:
Edward Diener wrote:
 I have a small nit having to do with a general comment which perhaps I
 did not really understand. In the documentation on class destructors I
 read:
 
 "When the garbage collector calls a destructor for an object of a class
 that has members that are references to garbage collected objects, those
 references are no longer valid. This means that destructors cannot
 reference sub objects. This rule does not apply to auto objects or
 objects deleted with the DeleteExpression."
 
 This comment makes perfect sense up to the last sentence. Except for a
 'scope' class a destructor can not possibly know how it is being called
 so if someone can explain to me how a destructor can know when it is OK
 to reference sub-objects or not, I would love to hear about it.

Under normal circumstances, this is exactly right. Tango has a feature whereby the finalization method can be hooked and altered however. See slide 18 of this presentation for an example: http://s3.amazonaws.com/dconf2007/SeanKelly.ppt
 I would
 have thought that referencing sub-objects in a destructor was just
 forbidden unless the class was a 'scope' class and had to reference a
 sub-object to possibly release a non-memory resource. But perhaps I am
 missing the full meaning of the comment and someone can explicate the
 issues involved for me.

In general I think you're right. Sean
Jan 28 2008
prev sibling next sibling parent BCS <ao pathlink.com> writes:
Reply to Edward,

 I have a small nit having to do with a general comment which perhaps I
 did not really understand. In the documentation on class destructors I
 read:
 
 "When the garbage collector calls a destructor for an object of a
 class that has members that are references to garbage collected
 objects, those references are no longer valid. This means that
 destructors cannot reference sub objects. This rule does not apply to
 auto objects or objects deleted with the DeleteExpression."
 
 This comment makes perfect sense up to the last sentence. Except for a
 'scope' class a destructor can not possibly know how it is being
 called so if someone can explain to me how a destructor can know when
 it is OK to reference sub-objects or not, I would love to hear about
 it.

Careful use of manual memory management?
Jan 28 2008
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On Jan 29, 2008 12:52 AM, Edward Diener
<eddielee_no_spam_here tropicsoft.com> wrote:
 Except for a
 'scope' class a destructor can not possibly know how it is being called

True. Nonetheless, I have seen code in C++ which does: ~MyHeapClass() { delete this; } even though the destructor cannot possibly know whether or not the class was allocated with new. In the C++ case, such code is legitimate /only/ if the programmer guarantees that MyHeapClass is always allocated on the heap and never on the stack. (It's a useful thing to do for thread classes, so that threads can delete themselves without needing a separate manager class). But of course, there is no way for the compiler to enforce this. But that's C++ and this is D. Nonetheless, I think there's an analogy, which is that, if you, the programmer, /know/ that you are /always/ going to explicitly call delete, and never rely on the gc to dispose of the class, then you can make more assumptions about what will still be valid at destructor time. You are quite right that the compiler has no way to enforce this, however, so I'd put it down to one of those "exotic" programming techniques, only ever useful in rare circumstances. I have never used that feature. If I want my class to have a manual clean-up function, I'll generally write a function called close().
Jan 29 2008
parent reply Edward Diener <eddielee_no_spam_here tropicsoft.com> writes:
Janice Caron wrote:
 On Jan 29, 2008 12:52 AM, Edward Diener
 <eddielee_no_spam_here tropicsoft.com> wrote:
 Except for a
 'scope' class a destructor can not possibly know how it is being called

True. Nonetheless, I have seen code in C++ which does: ~MyHeapClass() { delete this; } even though the destructor cannot possibly know whether or not the class was allocated with new.

I completely agree the above is bad C++ code, and can only be possibly considered correct if some rule says that the class can never be created except in dynamic memory. Usually that rule has to do with some C++ compiler extension which is not standard C++ and forbids a class object to be instantiated on the stack. Such is the case with Microsoft's ATL where you will see the idiom above actually occurring in code when the reference count for an ActiveX object, which must be created in dynamic storage, goes to 0. But in general it is a horrible way of coding.
Jan 29 2008
parent reply James Dennett <jdennett acm.org> writes:
Edward Diener wrote:
 Janice Caron wrote:
 On Jan 29, 2008 12:52 AM, Edward Diener
 <eddielee_no_spam_here tropicsoft.com> wrote:
 Except for a
 'scope' class a destructor can not possibly know how it is being called

True. Nonetheless, I have seen code in C++ which does: ~MyHeapClass() { delete this; } even though the destructor cannot possibly know whether or not the class was allocated with new.

I completely agree the above is bad C++ code, and can only be possibly considered correct if some rule says that the class can never be created except in dynamic memory.

Not even then; "delete this" is not valid from within a destructor, as the destructor has already set about destroying the object, and so attempting to re-destroy the object makes no sense. It's safe to use "delete this" in response to an object being told to drop the last reference to itself, but it makes no sense to do so from its own destructor. I suspect Janice just mis-quoted some C++ code, moving "delete this" to somewhere it doesn't belong. -- James
Jan 29 2008
parent Edward Diener <eddielee_no_spam_here tropicsoft.com> writes:
James Dennett wrote:
 Edward Diener wrote:
 Janice Caron wrote:
 On Jan 29, 2008 12:52 AM, Edward Diener
 <eddielee_no_spam_here tropicsoft.com> wrote:
 Except for a
 'scope' class a destructor can not possibly know how it is being called

True. Nonetheless, I have seen code in C++ which does: ~MyHeapClass() { delete this; } even though the destructor cannot possibly know whether or not the class was allocated with new.

I completely agree the above is bad C++ code, and can only be possibly considered correct if some rule says that the class can never be created except in dynamic memory.

Not even then; "delete this" is not valid from within a destructor, as the destructor has already set about destroying the object, and so attempting to re-destroy the object makes no sense.

You are completely correct and it was my error also in not noticing that this was done in the destructor itself.
Jan 30 2008
prev sibling next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Edward Diener wrote:
 I have a small nit having to do with a general comment which perhaps I
 did not really understand. In the documentation on class destructors I 
 read:
 
 "When the garbage collector calls a destructor for an object of a class
 that has members that are references to garbage collected objects, those
 references are no longer valid. This means that destructors cannot
 reference sub objects. This rule does not apply to auto objects or
 objects deleted with the DeleteExpression."

 This comment makes perfect sense up to the last sentence. 

You should have seen it before I asked for it to be rewritten!
 Except for a
 'scope' class a destructor can not possibly know how it is being called
 so if someone can explain to me how a destructor can know when it is OK
 to reference sub-objects or not, I would love to hear about it. I would
 have thought that referencing sub-objects in a destructor was just
 forbidden unless the class was a 'scope' class and had to reference a
 sub-object to possibly release a non-memory resource. But perhaps I am
 missing the full meaning of the comment and someone can explicate the
 issues involved for me.

All it means is that if for whatever reason you _know_ your destructor was called explicitly, and not indirectly by the GC, then you're ok. Whether this is of any practical use is another matter. I guess if you always hold explicit references to some set of things, then you know the GC isn't going to collect them, so you could write those classes to refer to stuff in their destructors. In other words "careful memory management" like BCS said so succinctly. --bb
Jan 29 2008
parent reply Edward Diener <eddielee_no_spam_here tropicsoft.com> writes:
Bill Baxter wrote:
 Edward Diener wrote:
 I have a small nit having to do with a general comment which perhaps I
 did not really understand. In the documentation on class destructors I 
 read:

 "When the garbage collector calls a destructor for an object of a class
 that has members that are references to garbage collected objects, those
 references are no longer valid. This means that destructors cannot
 reference sub objects. This rule does not apply to auto objects or
 objects deleted with the DeleteExpression."

 This comment makes perfect sense up to the last sentence. 

You should have seen it before I asked for it to be rewritten!

<g>
 
 Except for a
 'scope' class a destructor can not possibly know how it is being called
 so if someone can explain to me how a destructor can know when it is OK
 to reference sub-objects or not, I would love to hear about it. I would
 have thought that referencing sub-objects in a destructor was just
 forbidden unless the class was a 'scope' class and had to reference a
 sub-object to possibly release a non-memory resource. But perhaps I am
 missing the full meaning of the comment and someone can explicate the
 issues involved for me.

All it means is that if for whatever reason you _know_ your destructor was called explicitly, and not indirectly by the GC, then you're ok. Whether this is of any practical use is another matter. I guess if you always hold explicit references to some set of things, then you know the GC isn't going to collect them, so you could write those classes to refer to stuff in their destructors. In other words "careful memory management" like BCS said so succinctly.

What is an 'explicit reference to some set of things' ? Can not the GC be destroying your object in a cross-reference situation where the references you hold are no longer guaranteed to exist in the destructor ?
Jan 29 2008
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Edward Diener wrote:
 Bill Baxter wrote:
 Edward Diener wrote:
 I have a small nit having to do with a general comment which perhaps I
 did not really understand. In the documentation on class destructors 
 I read:

 "When the garbage collector calls a destructor for an object of a class
 that has members that are references to garbage collected objects, those
 references are no longer valid. This means that destructors cannot
 reference sub objects. This rule does not apply to auto objects or
 objects deleted with the DeleteExpression."

 This comment makes perfect sense up to the last sentence. 

You should have seen it before I asked for it to be rewritten!

<g>
 Except for a
 'scope' class a destructor can not possibly know how it is being called
 so if someone can explain to me how a destructor can know when it is OK
 to reference sub-objects or not, I would love to hear about it. I would
 have thought that referencing sub-objects in a destructor was just
 forbidden unless the class was a 'scope' class and had to reference a
 sub-object to possibly release a non-memory resource. But perhaps I am
 missing the full meaning of the comment and someone can explicate the
 issues involved for me.

All it means is that if for whatever reason you _know_ your destructor was called explicitly, and not indirectly by the GC, then you're ok. Whether this is of any practical use is another matter. I guess if you always hold explicit references to some set of things, then you know the GC isn't going to collect them, so you could write those classes to refer to stuff in their destructors. In other words "careful memory management" like BCS said so succinctly.

What is an 'explicit reference to some set of things' ? Can not the GC be destroying your object in a cross-reference situation where the references you hold are no longer guaranteed to exist in the destructor ?

Sure, I'm just saying if you're very careful about who holds references to what then you can guarantee the GC won't clean it up. class MyClass { this(MyOtherClass c) { c_ = c; } ~this() { c_.do_something_fun(); } } MyOtherClass c; MyClass d; void main() { c = new MyOtherClass; d = new MyClass(c); delete d; } In this program it is ok for MyClass to be using its c_ in its destructor. There's no way for c_ to have been destroyed already when we get to d's constructor *in this program*. I think this is probably of little practical value, though. I think all Walter wants to say there is that the indeterminate order of destruction in a GC world is the only reason why you can't refer to things in your destructors. Other than that, destructors work in a normal deterministic C++-ish way. --bb
Jan 29 2008
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 1/30/08, James Dennett <jdennett acm.org> wrote:
 I suspect Janice just mis-quoted some C++ code, moving "delete this"
 to somewhere it doesn't belong.

Nah, I just misremember it. It should of course be ~MyHeapClass() { operator delete(this); } That does the memory deallocation without re-calling the destructor. :-)
Jan 30 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Jan 30, 2008 4:43 AM, James Dennett <jdennett acm.org> wrote:
 I suspect Janice just mis-quoted some C++ code, moving "delete this"
 to somewhere it doesn't belong.

No wait - actually you're right. Yes, I had moved "delete this" to the wrong place. It actually goes in a member function, but not the destructor. From memory, the example was a Thread class, and it called delete on return from the virtual run() function, so that the threads resources got cleaned up after the thread had terminated. So, thanks for the correction. Nonetheless, it is possible (and in some cases legitimate) to call the destructor code without the deallocator, or vice versa. To destruct then deallocate (the normal situation) delete p; To destruct without dealocating: p->~Whatever(); (where Whatever is typeof(*p)) To deallocate without destructing: operator delete(p); Whether or not this is useful is another question. I don't know if the same distinctions can be made in D (or even whether or not we'd want them). In any case, it's all very off-topic with regard to the original question, so my apologies for the digression. And now back to our regularly scheduled discussion...
Jan 30 2008