Exception Handling1

Preview:

Citation preview

Exception HandlersOne exception handler for every type of exception that we want to catch.Exception handlers immediately follow the try block & are denoted by the keyword catch, e.g.

try {// code that may generate exceptions

} catch(type1 id1) {// handle exceptions of type1

} catch(type2 id2) {// handle exceptions of type2

}

Exception HandlersEach catch clause takes a single argument of one particular type.The identifier id1,id2 may be used inside the handler.The identifier is not necessary.Handlers must appear directly after try block with no other code in between.Handler searched like the switch statement except for the break keyword.

Exception Specification

Informs the user of a function about the type of exceptions the function can throw.

Is a part of function declaration appearing after the argument list of the function.

Uses keyword throw followed by parenthesized list of all potential exception types.

Exception specification contd.void f() throw(toobig, toosmall, divzero);Function can throw exceptions of type toobig, toosmall, divzero only.void f();Any type of exception may be thrown from the function.void f() throw();No exceptions are thrown from a function.

unexpected()Called when you throw something other than what appears in the exception specification.Implemented with a pointer to a function.By default, unexpected calls terminate.We can change its behavior using the set_unexpected() function which takes the address of a function with no arguments and void return value.Returns previous value of the unexpected( ) pointer which can be restored later.Header file <exception>

set_unexpected() e.gvoid my_unexpected(){

cout << "unexpected exception thrown";

exit(1);

}

int main() {//set our function handler for unexpected exceptions

set_unexpected(my_unexpected);//code that raises an unexpected exception

}

Uncaught exceptions

If none of the exception handlers following a particular try block matches an exception,then

Exception moves to the next-higher context, i.e. the function/try block surrounding the try block that failed to catch the exception.

This process continues until, at some level, a handler matches the exception.

At that point, the exception is considered “caught” and no further searching occurs.

terminate( )

terminate() is automatically called if an exception is uncaught.

Is actually a pointer to a function.

Default value is the Standard C library function abort( ).

No cleanups occur for an uncaught exception

set_terminate( )We can install our own terminate() function using the set_terminate() function.Returns a pointer to the previous terminate() function.Custom terminate() takes no arguments and returns a void value.Any terminate( ) handler installed must not return or throw an exception, but instead must call some sort of program-termination function.

set_terminate( ) e.g.

void terminator() {

cout << “Custom terimate ftn" << endl;

abort();

}

void (*old_terminate)()

= set_terminate(terminator);

set_terminate( ) e.g.#include <eh.h>#include <process.h>#include <iostream.h>

void term_func(){ cout << "term_func() was called by terminate().\n"; // ... cleanup tasks performed here // If this function does not exit, abort is called. exit(-1);}void main(){ int i = 10, j = 0, result; set_terminate( term_func ); try{ if( j == 0 ) throw "Divide by zero!"; else result = i/j; } catch( int ){ cout << "Caught some integer exception.\n"; } cout << "This should never print.\n";}

Catching any ExceptionIf a function has no exception specification, any type of exception can be thrown.Solution is to create a handler that catches any exception.Done by using ellipses in the argument list.catch(...) {cout << “An exception was thrown" << endl;}Should be put at the end of exception handlers.

Rethrowing an exceptionSometimes we would want to rethrow the exception that was just caught.Particularly when we use the ellipses to catch any exception because there is no information available about the exception.Accomplished by saying throw with no argument.catch(...) {

cout << "an exception was thrown" << endl;

throw;}Further catch clauses for the same try block are still ignored

Cleaning Up

All objects in that scope whose constructors have been completed will have destructors called.

If an exception is thrown before a constructor is completed, the associated destructor will not be called for that object.

Cleaning Up (e.g)class Cat {

public:Cat() { cout << "Cat()" << endl; }~Cat() { cout << "~Cat()" << endl; }

};class Dog {

public:void* operator new(size_t sz) {

cout << "allocating a Dog" << endl;throw int(47);

}void operator delete(void* p) {

cout << "deallocating a Dog" << endl;::delete p;

}};

Cleaning Up (e.g) contd.class UseResources {

Cat* bp;Dog* op;

public:UseResources(int count = 1) {

cout << "UseResources()" << endl;bp = new Cat[count];op = new Dog;

}~UseResources() {

cout << "~UseResources()" << endl;delete []bp; // Array deletedelete op;

}};

Cleaning Up (e.g) contd.int main() {

try {UseResources ur(3);

} catch(int) {cout << "inside handler" << endl;}

}

The output is the followingUseResources()Cat()Cat()Cat()allocating a Doginside handler

Solution

Is to place allocations inside their own objects with their own constructors and destructors.

Each allocation becomes atomic.

If it fails, the other resource allocation objects are properly cleaned up.

Templates offer a solution.

Solution using templatestemplate<class T, int sz = 1> class PWrap {

T* ptr;public:

PWrap() {ptr = new T[sz];cout << "PWrap constructor" << endl;

}~PWrap() {

delete []ptr;cout << "PWrap destructor" << endl;

}T& operator[](int i){

if(i >= 0 && i < sz) return ptr[i]; }};

Solution using templates contd.class Cat {

public:Cat() { cout << "Cat()" << endl; }~Cat() { cout << "~Cat()" << endl; }void g() {}

};class Dog {

public:void* operator new[](size_t sz) {cout << "allocating an Dog" << endl;throw int(47);}void operator delete[](void* p) {cout << "deallocating an Dog" << endl;::delete p;}

};

Solution using templates contd.class UseResources {

PWrap<Cat, 3> Bonk;PWrap<Dog> Og;

public:UseResources() : Bonk(), Og() {

cout << "UseResources()" << endl;}~UseResources() {

cout << "~UseResources()" << endl;}void f() { Bonk[1].g(); }

};

Solution using templates contd.int main() {try {

UseResources ur;}catch(int) {

cout << "inside handler" << endl;}catch(...) {

cout << "inside catch(...)" << endl;}

}

Solution using templates contd.Cat()Cat()Cat()PWrap constructorallocating a Dog~Cat()~Cat()~Cat()PWrap destructorinside handler

Exception MatchingException-handling system looks through the “nearest” handlers in the order they are written.When it finds a match, the exception is considered handled, and no further searching occurs.Matching an exception doesn’t require a perfect match between the exception and its handler.An object or reference to a derived-class object will match a handler for the base class.If handler is for an object rather than a reference, the exception object is “sliced” as it is passed to the handler.

Exception Matchingclass X {

public:class Trouble {};class Small : public Trouble {};class Big : public Trouble {};void f() { throw Big(); }

};

Exception Matchingint main() {

X x;try {

x.f();} catch(X::Trouble) {

cout << "caught Trouble" << endl;} catch(X::Small) { // Hidden by previous handler:

cout << "caught Small Trouble" << endl;} catch(X::Big) {

cout << "caught Big Trouble" << endl;}

}

Catch by reference not by value

If you throw an object of a derived class and it is caught by value in a handler for an object of the base class, that object is “sliced” – that is, the derived-class elements are cut off and you’ll end up with the base-class object being passed.

Chances are this is not what you want because the object will behave like a base-class object and not the derived class object it really is.

Catch by reference not by valueclass Base {

public:virtual void what() {

cout << "Base" << endl;}

};class Derived : public Base {

public:void what() {

cout << "Derived" << endl;}

};void f() { throw Derived(); }

Catch by reference not by valueint main() {

try {f();

} catch(Base b) {b.what();

}try {

f();} catch(Base& b) {b.what();}

}

The output isBaseDerived

Recommended