git-svn-id: https://cppannotations.svn.sourceforge.net/svnroot/cppannotations/trunk@306 f6dd340e-d3f9-0310-b409-bdd246841980
This commit is contained in:
Frank B. Brokken 2009-11-28 16:45:44 +00:00
parent 5967641984
commit f5539539f2
4 changed files with 86 additions and 36 deletions

View file

@ -43,6 +43,12 @@ COMMENT(>>>>>>>>>>>>> NEXT <<<<<<<<<<<<<)
sect(Exception guarantees)
includefile(exceptions/guarantees)
subsect(The basic guarantee)
includefile(exceptions/basic)
subsect(The strong guarantee)
includefile(exceptions/strong)
lsect(FUNTRY)(Function try blocks)
includefile(exceptions/function)

55
yo/exceptions/basic.yo Normal file
View file

@ -0,0 +1,55 @@
The emi(basic guarantee) dicates that functions that failing to complete their
assigned tasks must return all allocated resources, usually memory, before
terminating. Since practically all functions and operators may throw
exceptions and since a function may repeatedly allocate resources the
blueprint of a function allocating resources shown below defines a try block
to catch all exceptions that might be thrown. The catch handler's task is to
return all allocated resources and then retrow the exception.
verb(
void allocator(X **xDest, Y **yDest)
{
X *xp = 0; // non-throwing preamble
Y *yp = 0;
try // this part might throw
{
xp = new X[nX]; // alternatively: allocate one object
yp = new Y[nY];
}
catch(...)
{
delete xp;
throw;
}
delete[] *xDest; // non-throwing postamble
*xDest = xp;
delete[] *yDest;
*yDest = yp;
}
)
In the pre-try code the pointers to receive the addresses returned by the
operator tt(new) calls are initialized to 0. Since the catch handler must be
able to return allocated memory they must be available outside of the tt(try)
block. If the allocation succeeds the memory pointed to by the destination
pointers is returned and then the pointers are given new values.
Allocation and or initialization might fail. If allocation fails tt(new)
throws a hi(bad_alloc)tt(std::bad_alloc) exception and the catch handler
simply deletes 0 pointers which is OK.
If allocation succeeds but the construction of (some) of the objects fails
by throwing an exception then the following is
hi(new: and exceptions)hi(exception: and new) em(guaranteed) to happen:
itemization(
it() The destructors of all successfully allocated objects are called;
it() The dynamically allocated memory to contain the objects is returned
)
Consequently, no memory will leak when tt(new) fails. Inside the above
tt(try) block tt(new X) may fail: this will keep the 0-pointers intact and so
the catch handler merely deletes 0 pointers. When tt(new Y) fails tt(xp) will
point to allocated memory and so it must be returned. This happens inside the
catch handler. The final pointer (here: tt(yp)) will only be unequal zero
when tt(new Y) properly completes, so there's no need for the catch handler to
return the memory pointed at by tt(yp).

View file

@ -54,7 +54,7 @@ at that many situations?
Exceptions may be generated in a great many situations, but serious
problems will be prevented when we're able to provide at least one of the
following exception guarantees:
following i(exception guarantees):
itemization(
it() The em(basic guarantee): no resources are leaked. In practice this
means: all allocated memory is properly returned when exceptions are thrown.
@ -65,38 +65,3 @@ assignment operator provides this guarantee)
proven that no exception will be thrown from it.
)
The basic guarantee dicates that functions that fail to complete their
work properly must return all allocated resources, usually memory, before
returning. Since practically all functions and operators may throw exceptions
and since a function may repeatedly allocate resources a try block
is used to catch all exceptions that might be thrown. The catch handler may
then return all allocated resources and retrow the exception. If all's well
verb(
void allocator(/* parameters, if any */)
{
X *xp = 0; // non-throwing preamble
Y *yp = 0;
Z *zp = 0;
try // this part might throw
{
xp = allocateX(/* arguments */);
yp = allocateY(/* arguments */);
zp = allocateZ(/* arguments */);
thisMightThrowToo();
}
catch(...)
{
delete zp;
delete yp;
delete xp;
throw;
}
xDest = xp; // non-throwing postamble
yDest = yp;
zDest = zp;
}
)

24
yo/exceptions/strong.yo Normal file
View file

@ -0,0 +1,24 @@
The emi(strong guarantee) dicates that an object's state should not change in
the face of exceptions. This is realized by performing all operations that
might throw on a separate copy of the data. If all this succeeds then the
current object and its (now successfully modified) copy are swapped. An
example of this approach can be observed in the canonical overloaded
assignment operator:
verb(
Class &operator=(Class const &other)
{
Class tmp(other);
swap(other);
return *this;
}
)
The copy construction might throw an exception, but this keeps the current
object's state intact. If the copy construction succeeds tt(swap) swaps the
current object's contents with tt(tmp)'s contents and returns a reference to
the current object. For this to succeed it must be guaranteed that tt(swap)
won't throw an exception. Returning a reference (or a value of a primitive
data type) is also guaranteed not to throw exceptions. The canonical form of
the overloaded assignment operator therefore meets the requirements of the
strong guarantee.