Tuesday, November 28, 2006

Exceptions (part 1)

As you already know a modern way to deal with "errors" in C++ is the exception handling; however you need to be careful on using this mechanism. Let see how it works and some tips as well.
The exception mechanism is based on the try - catch blocks:




try {
//some code in here that we attempt to execute
}
catch (...) {
// this block is the error handling, the code in this
// block is executed if the code in the
// try block above have thrown an exception
}



easy and net.

Let see how throw an exception so we can see more in depth what this mechanism offers, how use it, what to avoid.
An exception is thrown with a throw:



throw A;



where A is the type of the object thrown, in that case we are throwing an object of type A inizialized with his default constructor, we could have done:



throw A(3, "foo");



or even:



A a(3, "foo");
throw a;



The catch(...) { } handler is supposed to handle all kind of exception that code inside the try block throws, in this way we lose the kind of exception thrown so it's a bit reductive because the error handler doesn't have a clue on what is going on; fortunately is possible to specify wich kind of exceptions we want to manage (we as well ignore some).
Specify which kind of exception we want manage is done in this way:



catch(Foo f) {
}



we can have multiple catch blocks after a try:




try {
//code we are attempting to execute
}
catch(Foo f) {
// error handler in case a Foo type was thrown
}
catch(Bar b) {
// error handler in case a Bar type was thrown
}



if the code in the try block thrown an exception that is not Foo and neither Bar then is like we are not using the try-catch blocks ( apart the introduction of the try's extra scope ).

We can obtain the same behaviour (logging for example we were not able to catch any expected exception) in this way:



try {
//code we are attempting to execute
}
catch(Foo f) {
// error handler in case a Foo type was thrown
}
catch(Bar b) {
// error handler in case a Bar type was thrown
}
catch(...) {
// log the event in here
throw; // this throws again the same exception.
}



There is still something behind all this. As you have seen the catch blocks are very similar to a function declaration where the arguments are passed by value.
The catch blocks can have indeed all kind of parameters:

catch( T )
catch( T & )
catch( T * )
catch( const T )
catch( const T & )
catch( const T * )

so you can think of throwing an exception have same effect of calling a function, but is not.
Consider this code:



void foo()
{
...
A aLocalObject;
...

throw aLocalObject
}



and the call of foo is inside a try block:



try {
foo();
}
catch( A anException) {

}




in this case, as the catch "signature" suggests, the anException is a copy of aLocalObject so
no problem with it, but what if we catch by reference?



catch( A & anException ) {
}



in this case we can think to have a reference to a destroyed object ( when an exception is thrown is not like a function call and all the local variable on that scope are destroyed ), well this is not the case indeed even if you catch an exception by reference the c++ runtime support will perform a copy of the object thrown, and this happens always, you can not avoid this copy even if the object will not be destroyed going out of scope ( a static variable for example ).

So in case of:

catch( A ) { }

you have 2 copies performed, in case of:

catch ( A & )

you have just one copy. For this very reason is not possible to modify the object thrown, because you have on the catch block a copy of it.

Next week the second and last part about exceptions.

Wednesday, November 22, 2006

operator new - new operator

At first shot this two entities can appear to be the same; however they are not.
Let see what they are, what you can change and the safe rules to handle them.

First of all let see what happens when you write something like:

C * pC = new C;
  • Enough memory is allocated to contain the object requested
  • The constructor of C is called to initialize the object the lays in the memory allocated
This described is the new operator behave and you can not change the way he acts.

The first point in the sequence above is the only think you can change, the way the memory is allocated, the new operator uses for this task what is called: operator new.

So the pseudo code for C * pC = new C; could be:
  1. Call operator new
  2. Construct an object of the type request at the location returned from previous step
The operator new signature is something like this:

void * operator new(size_t);

so if you want change the way the new operator allocates the memory for your type then you need to rewrite the operator new.
As you already know when you specify a name in a scope ( for example a method name in a class ) this will hide the same name in the scopes that are containing your actual ( the base class scope for example ). So rewriting your operator new what you do is to hide the other forms of operator new. For instance these forms are:
  1. void * operator new(std::size_t, std::nothrow_t) throw();
  2. void * operator new(std::size_t, void *);
the former is the nothrow new the latter is the in place new. Actualy you can break more than this if you define your own operator new, something like:
  • void * operator new(std::size_t, T);
remember in this case that first argument of operator new shall be always std::size_t, in this case you will hide not only the "less common" operator new version but also the plain new one. I quoted less common because in reality the STL does heavy usage of in place new.