www.digitalmars.com         C & C++   DMDScript  

D - Destructors

reply Kent Quirk <Kent_member pathlink.com> writes:
I'm a bit twisted around on the destructor thing.

The docs say this about destructors:

"The garbage collector calls the destructor function when the object is deleted.
" ... "The program can explicitly inform the garbage collector that an object is
no longer referred to (with the delete expression), and then the garbage
collector calls the destructor immediately, and adds the object's memory to the
free storage. The destructor is guaranteed to never be called twice."

This statement on destructors is far stronger than Java's finalize(), which
basically says "we might get around to calling it, maybe, if we feel like it,
but don't count on it." Which is worse than useless, as far as I'm concerned.
Better not to have it at all.

Question 1: It's not absolutely clear: does D guarantee that destructors will
always eventually be called, even on program termination? (Please say yes!)

Question 2: The introduction explicitly refers to enabling the RAII (Resource
Acquisition Is Initialization) idiom from C++. To me, that says "acquire your
resources in the constructor, free them in the destructor."

That, in turn, enables a very useful idiom in C++, which is instantiating a
class to acquire a valuable resource that is then freed automatically when the
object is deleted. For example, a lock on a file:

:  void writeToLog(string message)
:  {
:     FileLock theLock(LOGFILE);    // this acquires the lock on the log file
:     writeToLog(LOGFILE, message);
// log file lock automatically released when we exit the routine
// ...even if we throw
}

The nice thing about this is that we don't have to remember to explicitly
release the lock. In general, this idiom allows you to avoid a common
programming error of forgetting to make corresponding edits.

Now it seems that in D, I could write the same code...but I wouldn't know when
the lock would be released. It might even seem to work properly under testing
but fail under strange conditions when the GC didn't run for a while.

Looks like the corresponding D idiom would require that I explicitly delete the
lock in a finally clause. Which gets into the whole "I have to remember to
maintain both halves of the code" thing. Which, IMO, weakens the RIAA model
considerably.

In the docs there's a statement:

'Which of "release resource using destructor" (C++) or "release resource using
finally" (D) is better is a topic for much debate, but I obviously am in the
latter camp. '

So clearly this was not only a deliberate design decision, but one based on
superiority. Please enlighten me. I understand the argument that "deterministic
GC based on objects going out of scope is Really Really Hard to do reliably".
But I have never before heard anyone argue that the lack of deterministic
destruction is A Good Idea.

Why is it better to be forced to do things twice?


-- Kent
CTO, CogniToy
Jan 14 2003
next sibling parent reply Burton Radons <loth users.sourceforge.net> writes:
Kent Quirk wrote:
 Question 1: It's not absolutely clear: does D guarantee that destructors will
 always eventually be called, even on program termination? (Please say yes!)

No. But I don't see any reason why that shouldn't be a requirement.
 Question 2: The introduction explicitly refers to enabling the RAII (Resource
 Acquisition Is Initialization) idiom from C++. To me, that says "acquire your
 resources in the constructor, free them in the destructor."

The RAII vernacular means nothing to me, and I don't know what baggage the acronym brings along. What D does here is allow you to automatically delete a class instance in a local variable when its scope exits. You apply the "auto" keyword to a class as well as definitions of it in the scope, so: auto class FileLock ... auto FileLock theLock = new FileLock (LOGFILE); We have the synchronized statement as well.
Jan 14 2003
parent reply Kent Quirk <Kent_member pathlink.com> writes:
In article <b01bgr$16ui$1 digitaldaemon.com>, Burton Radons says...
The RAII vernacular means nothing to me, and I don't know what baggage 
the acronym brings along.  What D does here is allow you to 
automatically delete a class instance in a local variable when its scope 
exits.  You apply the "auto" keyword to a class as well as definitions 
of it in the scope, so:

     auto class FileLock ...

     auto FileLock theLock = new FileLock (LOGFILE);

Perfect. That neatly handles my worries. RAII (Resource Acquisition Is Initialization) is an idiom that says anytime you need a resource, you should do it as the initialization portion of an object designed to hold the resource. Then you free the resource on the destructor of that object. Then you can use object lifetime as a way of managing a resource. It's a way of avoiding ever having dangling pointers. Since I adopted it, I almost never use "raw" pointers and I almost never have null pointer or dangling pointer bugs. Kent Quirk CTO, CogniToy
Jan 14 2003
parent Burton Radons <loth users.sourceforge.net> writes:
Kent Quirk wrote:
 In article <b01bgr$16ui$1 digitaldaemon.com>, Burton Radons says...
 
The RAII vernacular means nothing to me, and I don't know what baggage 
the acronym brings along.  What D does here is allow you to 
automatically delete a class instance in a local variable when its scope 
exits.  You apply the "auto" keyword to a class as well as definitions 
of it in the scope, so:

    auto class FileLock ...

    auto FileLock theLock = new FileLock (LOGFILE);

Perfect. That neatly handles my worries. RAII (Resource Acquisition Is Initialization) is an idiom that says anytime you need a resource, you should do it as the initialization portion of an object designed to hold the resource. Then you free the resource on the destructor of that object. Then you can use object lifetime as a way of managing a resource.

Ah, so it's pretty much tied into C++'s constructor/destructor model, the problem with which is that it makes exceptions too expensive. I don't think I've seen C++ code which uses exceptions, in fact. There's restrictions with auto objects. They can't be put into fields or globals, or reassigned. I think this is a plus, as it means that the destructor is called in a stable environment, as opposed to the burning house a GC destructor runs in. The standard should have big caution signs around the documentation on destructors - I've burnt myself with them a couple times now. For example (and off-topic from here on out), if destructors are called on all objects at the end of the program, then deregistration where you take yourself out of a global dictionary might break down if it has been freed already. But there's a solution to the problem: put code that deletes all entries in the list, or simply empty it if that is enough, in the static destructor, effectively ordering destruction. class Control { static Control [HWND] map; Control [] children; this (Control parent) { if (parent) parent.children ~= this; hwnd = CreateWindow (...); map [hwnd] = this; } ~this () { delete map [hwnd]; /* If map has already been deleted by the GC at the end of the program, this will segfault. */ } static ~this () { map = null; } } Whether merely assigning the static list to null is good enough depends upon what happens in the destructor. Notice that I've defined a completely different GC environment: controls are NOT collected unless if they are explicitly deleted because of their global registration. This means that any objects, such as the children list, are not deleted by the time we get to the destructor normally, so it's spared from normal GC destructors' chaotic environment. But after the static destructors have been executed, the controls will be collected in random order, which can lead to hard to figure out bugs. So I do need to delete the list manually in my case: static ~this () { while (map.length) delete map.values [0]; }
Jan 14 2003
prev sibling parent Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
 -- Kent
 CTO, CogniToy

Welcome, Kent. Good to see the MindRover folks are reading about D, too! I loved that game! The visual programming model was intriguing. I never actually even looked at ICE, though. For all the programmers listening here, MindRover is definitely a game worth looking into :)
Jan 14 2003