Re: Exception Misconceptions: Exceptions are for unrecoverable errors.
"James Kanze" <email@example.com>
So you use the same tech -- the destructor sits there to carry
out the responsibility that is left over.
There is a similarity: additional actions may be needed when an
exception has been thrown. But the word "resource" (or even
"responibility") seems too limiting. I prefer talking in terms
of program coherence (in which invariants are maintained): the
coalition takes place in the other sense: when the program is
coherent, for example, no one holds a mutex lock, or other
resources that won't be used.
Hm, to me that is what sounds strange. To me mutex locking means a ctirical
section of code, and has nothing to do with invariants or coherence.
And invariant means something that holds. And supposed to. It may be broken
for some special technical reason (like not having atomic multi-assign), but
it better be avoided. While critical sections are totally meant to be
entered, and perfectly natural.
If the transaction is really a concrete object, perhaps, but
what if it is just an invariant that is temporarily broken.
For API-based transactions it is simple, call BeginTrans, and
keep a bool to track explicit Rollback or Commit was called.
For internal state transactions it is more complicated -- you
record the stepst taken so rollback can be arranged. Though
that is the less suggested method, I try to use
create-then-swap wherever possible.
In general: it's best to do anything that might throw before
changing any state. (That principle predates the swap idiom by
Sure, that just comes from the fact that an UNDO action is often far from
trivial even in theory, and doing it may fail just like the operation that
went forward. We rather avoid such potentially hopeless situations. ;-)
The swap idiom is just a fairly simple way of
expressing it in C++ (and letting destructors handle the
clean-up in both the error cases and the normal case). But if
you're interested, an analysis of the constructor code for
boost::shared_ptr is illuminating; doing things correctly
requires some thought.
I did study that constructor when it was a new thing (guess like a decade
ago), and it was definitely illuminating. IIRC it was before Herb's
Exceptional... books, and the Abrahams guarantees were either in the future
or new stuff.
By today the scene hopefully is different, that material is considered
fundamental for a long time. And have high attention in interviews for a
C++ position and code reviews.
Independantly of the language:
destructors do help in the implementation details, but the
initial analysis is still necessary.
I didn't say otherwise -- just dtors are a great tool that works like pyro
seat belts. And are good to have even if you stop the car in time.
(A good example of this would be a simple implementation of
shared_ptr. Boost goes to a lot of effort to ensure that
shared_ptr always leaves the program in coherent state, but
given that there are two dynamic allocations involved, it
requires careful consideration to ensure exception safety.)
Err, what is that "lot effort"? Placing allocations into
local scoped_ptrs then swap or relase them into members
Defining clearly what pointers have to be freed, when. In
general, the implementation doesn't require much effort, once
you've defined clearly what has to be done.
In normal cases we want to avoid that very problem. In ctor, and related
stuff -- copy ctor, op=, possibly others. The way to avoid it is to NOT use
raw pointers as members like shared_ptr does. But use smart members or a
That side-step the problem for good.
The smart pointer suite itself is IMO quite a special case. :)
Not to be done -- or trusted to a real expert. Who certainly spent his time
studying the existing implementations, and is aware of those problems.