(e-mail address removed)>, (e-mail address removed)
says...
[ ... ]
Seriously, I don't know. My own experience is that managing
other resources (or determinate lifetime in general) isn't that
difficult, even without RAII. In some cases, in fact, I find an
explicit finally block clearer---it makes it far more visible
how the resource is being handled, since the code for releasing
it is local to the same function in which it is freed. But
having seen a lot of code, in a lot of different languages, I'm
forced to recognize that my own experience really is my own
experience; missing finally blocks seem to be a very frequent
error in Java.
Right -- a finally block is explicit, but requires duplication
of the resource deallocation everywhere the resource is used.
Given a choice between something that's explicit by likely to
be used incorrectly, and something that's less explicit but
makes incorrect use difficult, I'll pick the less explicit
version every time.
Seriously, I didn't say I preferred it. I just wanted to point
out that the argument isn't as cut and dried as it is often made
out. And depends on the resource; one of the classical
"resources" that gets mentioned, for example, are file
descriptors---since "freeing" a file descriptor (close()) can
fail, and you probably have to treat the error, differently in
different cases, there's a very strong argument for a finally
block in this case (with an assertion failure that the file has
been correctly closed in the destructor).
The most frequent clean-up in C++ is memory, with locks probably
coming in second. The first isn't needed with garbage
collection, and a lot of languages have make synchronization
part of the language, to avoid the second. I disagree with this
choice, because there are cases where you want to hold a lock
independantly of scope---I use boost::shared_ptr to manage locks
more often than I do to manage memory.
If memory was really just memory, I'd more or less agree. In
reality, it's not -- memory is used to hold _objects_. In an
OO program, managing those objects tends to be most of what
the program does. RAII and GC provide different ways of
managing objects.
And that's where you're wrong. Obviously, memory is used to
hold objects. And code. And a lot of other things. Memory,
per se, is part of the underlying abstraction, like the CPU.
Back in the old days, we really had to manage memory. Including
the memory in which functions were situated---do you remember
programming with overlays, before virtual memory? Today,
virtual memory makes all of that a bad memory. Not using
garbage collection today is in many ways like refusing virtual
memory was back then (and a lot of programmers did refuse it,
and insisted it was better to have to manually manage your
overlays).
Garbage collection doesn't manage objects, or object lifetime.
That's still up to you.
RAII automates quite a bit of the management of many types of
objects, while GC automates one part of managing a much
smaller subset of objects.
GC has nothing to do with managing objects. It only manages
memory.
[ ... I had said: ]
Which was? Either memory management has been turned over to the
OS (in which case, the memory in question isn't even part of the
process image, for the garbage collection to free), or it hasn't
been (in which case, you still have a pointer to it, so garbage
collection won't free it).
TTBOMR, the code in question did something along the lines of
allocating an array, and then writing it to disk. The sole
remaining pointer to the array had been passed to the OS for
writing, so the GC (apparently) decided it was ready to be
collected. As I said previously, the real problem was probably
in the library rather than the GC per se -- the library should
have kept a pointer to the data around until the OS call to
write the data had returned, but it apparently didn't.
The real problem is even more fundamental, I think. Regardless
of the system you're using to manage memory---garbage collection
or manual---how do you know when it is safe to reuse this
memory? Solve that, and you can find solutions with garbage
collection or with manual management.
The one case this could be a problem is if you're adding garbage
collection to C++, and the library isn't aware of it, and
expects you to free the pointer manually in some call-back.
Except that even then, if you're going to free the pointer,
you've got to have a copy of it somewhere.
[ ... ]
That is a real problem. In general, any "new" technology
gets overused---people seem to believe in silver bullets.
And for various historical reasons, many programmers don't
seem to be aware of the distinction between object lifetime
and memory management; more than a few Java programmers do
make claims to the effect that you can ignore object
lifetime considerations; by tying memory management to
object lifetime (and thus forcing a determinate lifetime on
all objects), we certainly draw attention to object lifetime
in general. But should we really refuse a technology which
will improve the productivity of the competent programmers
just because it can and will be abused? And is artificially
creating unnecessary work (consideration of object lifetime
when it is not necessary) really the best way to stimulate
awareness of an issue.
I don't know for sure -- on a number of counts. First of all,
I'm not sure it really improves productivity by any noticeable
margin.
It's measurably improved mine, in the applications where I've
been able to use it. How much improvement obviously will depend
on the application.
Second, I'm not sure that consideration of object lifetime
really causes much extra work it the long run either. As
you've pointed out, the variance is so great that the average
means little, so meaningful measurement is difficult; when I
say "I don't know", I'm not trying to voice disagreement, but
really admitting that I just don't know.
It depends on the objects. Maybe it's just my style, but I tend
to use polymorphic agents in some cases; typically, managing
their lifetime isn't a lot of extra work (boost::shared_ptr
works well in this case, since agents never point to other
agents, only to entity objects), but the amount isn't 0, either.
And in a few cases (I'm working on one right now), I have to
deal with more or less complicated graphs---again, because it is
a graph (with no parent links), I can get by with
boost::shared_ptr (in this case, my pre-Boost reference counted
pointer, in fact, since the code in question was originally
written some 15 years ago). But it makes things more
complicated. Not enormously so, but the little bits add up.
Ah yes, the "it doesn't work, but look at my beautiful GUI"
syndrome. The worst part is that (IMO) their GUI usually
reflects the confusion and complexity of the code...
Sometimes, it's what the customer asks for. I remember one case
where the customer was constantly on to us because some toolbar
was one pixel too high, or things like that. But never noticed
that the code systematically multiplied by 3.5 to convert
currency, i.e. DM to Euro, multiply the DM by 3.5; Euro to DM,
multipy the Euro by 3.5. The customer also paid a very large
sum to a research institute to establish ergonomic guidelines
for the GUI interface---all of which turned out to be just
common sense or good esthetics, and most of which, they then
insisted that we violate.