So far, all the comments seem to be missing Stroustrup's biggest point: garbage collection only works for memory. Destructors work for everything - memory, file handles, semaphores/locks, database connections, everything.
Garbage collection handles 90% of the problem transparently to the programmer. That's wonderful (really - I'm not being sarcastic here). But the problem is, garbage collecting languages usually don't have destructors, and so you're left having to manage the other 10% of the problem yourself. You have to remember to close all your own files, clean up all your database connections, and so on. Garbage collection usually makes the 90% trivial, at the price of giving you no tool at all for dealing with the other 10% (non-memory portion) of resource management.
> You have to remember to close all your own files, clean up all your database connections, and so on. Garbage collection usually makes the 90% trivial, at the price of giving you no tool at all for dealing with the other 10% (non-memory portion) of resource management.
In languages that use lambdas/closures, you can make some sort of RAII as well.
It is a well known pattern in Lisp and ML languages.
Many modern languages have a convenient solution for those cases as well: Python has the "with" statement combined with context managers; C# has the "using" block with the "IDisposable" interface that does essentially the same thing; even Java recently got its corresponding "try-with-resources".
Before those were introduced we had try/finally which works equally well but is slightly more verbose.
Yes, you can do this with try/finally. But if you have an object that has an open file as a member, then you open the file in the constructor, and then use the open file in the member methods, and then... what? You may have a scope that you are exiting where that object becomes irrelevant, but it may be several layers away. Having to close that object's handle in a finally in that scope seems likely to be forgotten at least some of the time.
That "scope that you are exiting ... several layers away" is where you put the try/finally.
If an object has resources to dispose of (such as an open file), it should implement (to use C# as an example – it looks similar in Python and Java) the IDisposable interface, which allows you to do either:
using (MyFileWrapperObject obj = ...) {
foo();
bar();
...
}
Or to take a Python example from what I'm working on right now:
with open("something.json") as f:
data = json.load(f)
The file will automatically be closed at the end of the "with" block. Just like in C#, this feature can be used for any kind of resource, not just files.
> That "scope that you are exiting ... several layers away" is where you put the try/finally.
Yes, I know that it's where you're supposed to put the try/finally... if you remember. Each time. And if you move the action in the finally clause if the variable changes scope or lifetime.
Destructors are considerably cleaner than this approach. You just put the close in the destructor, and let it do its job.
Garbage collection handles 90% of the problem transparently to the programmer. That's wonderful (really - I'm not being sarcastic here). But the problem is, garbage collecting languages usually don't have destructors, and so you're left having to manage the other 10% of the problem yourself. You have to remember to close all your own files, clean up all your database connections, and so on. Garbage collection usually makes the 90% trivial, at the price of giving you no tool at all for dealing with the other 10% (non-memory portion) of resource management.