Plab – Tirgul 8 Exceptions. Error handling in C Up to now we handled our errors in the “C...

21
Plab – Tirgul 8 Exceptions
  • date post

    21-Dec-2015
  • Category

    Documents

  • view

    226
  • download

    0

Transcript of Plab – Tirgul 8 Exceptions. Error handling in C Up to now we handled our errors in the “C...

Plab – Tirgul 8Exceptions

Error handling in C• Up to now we handled our errors in the “C way”:

• assert• return codes

• global error variable (errno and perror)

• “The problem with C’s approach to error handling could be thought of as coupling—the user of a function must tie the error-handling code so closely to that function that it becomes too ungainly and awkward to use ”

Exceptions• C++ introduces the use of exceptions to handle errors.

• You are already familiar with exceptions in Java.

• Unfortunately, in C++, due to the more complex memory model, the exceptions are far less simple .

Exceptions – why?

• Exceptions are used in C++ for:– Handling errors in constructors.

– Handling memory errors.

– Math errors.

– Library errors or third party code.

Throwing exception example• If pop() from empty ChangesCollection is

illegal we would like to throw an exception.

const Change* ChangesCollection::pop() { if (isEmpty()) throw 0;

...

}

• In C++ anything can be thrown: basic types (int, double, etc.), pointers, objects, references.

Throwing exception

const Change* ChangesCollection::pop() { if (isEmty()) throw EmptyCollectionE(__FILE__, __LINE__);

...

}

• It’s a good practice to define special exception classes (usually there will be a hierarchy of these):

• If an exception is not caught it will abort the program.

• Unlike Java you will get no info about the exception (such as where it was thrown, the call stack).

Catching an exception• We use try and catch similarly to Java.• Consider:

try {...

}

catch (CollectionE& e) { e.printErrorMessage(); throw; // re-throw the exception

}• Note that catching by reference enables

polymorphism.

Catching an exception cntd.• We can catch several different exceptions at a

time: try { ...}catch (EmptyCollectionE& e) { ...}catch (CollectionE& e) { ...}catch (...) { // catch ANY exception ...}

Note the order of the

catch clauses

Re-Throw

Having caught an exception, it is common for a handler to decide that it can’t completely handle the error.

In that case, the handler typically does what can be done locally and then throws the exception again.

Re-Throw

void h() {try {

…} catch (Matherr) {

if(can_handle_it_completely) {…return;

}else {//do what can be done here.throw; // re-throw the exception.}

}

Grouping of Exceptions

• Exceptions fall naturally into families. This implies that inheritance can be useful to structure exceptions and to help exception handling:

class Matherr {};

class Overflaw : public Matherr{};

class Underflow : public Matherr{};

Class Zerodivide : public Matherr{};

Grouping of Exceptions Cont

void f() {try {

// …} catch (Overflaw) {

//handle Overflaw or anything derived from // Overflaw

} catch (Matherr) {

//handle any Matherr that is no Overflaw

}}

Exception specification• Unlike Java, functions (methods) do not

have to declare what exceptions they can throw:– int f(); // can throw any exception

• Still, it’s a good style to declare the exceptions your functions may throw:– int f() throw (BadArg, int*);// f can throw only BadArg, int*

• This function does not throw exceptions:– void g() throw();

Stack unwinding• When exception is thrown all automatic variable

of the function on the stack are freed.

• What about other resources, like:– open files

– memory on heapFILE* f = fopen(filename, “r”);try { ...}catch(...) { fclose(f); throw; }fclose(f);

Note that in most C++ compilers there is no finally.

Stack unwinding cntd.

• This solution is tedious if the resources are allocated in many different places.

• A better solution is to wrap the resource in an object, which will be automatically deallocated:

Resource Management Example

class FilePtr {FILE *p;

public:FilePtr(const char *n, const char *a)

{ p = fopen(n,a); }FilePtr(File *pp) { p = pp; }~FilePtr() { fclose(p); }

operator FILE*() { return p; }};

Stack unwinding cntd.FilePtr f(filename, “r”);try { ... // here f is used}catch(...) { ...}

The file is closed by FilePtr’s destructor, thus it will be closed regardless of the way we exit (normally or with exception).

Exceptions in constructor• Very convenient• If the object is constructed in (dynamic)

heap and its constructor throws exception, the memory will be released automatically.

• The destructor won’t be called.

Exceptions in constructor

• Example: Circle’s constructor throws exception when provided with negative radius:try { c = new Circle(1, 2, -1);}catch(...) { // we should not delete c !}

Standard exceptionsexception

logic_error runtime_error

ios::failure

invalid_argument

out_of_range

bad_alloc

And others.

Read yourselves: auto_ptr• Similarly to the way we wrapped a FILE pointer, we can

wrap any pointer, so that it is automatically deleted when we leave the scope.

• The auto_ptr which is part of the standard library is a template class for this purpose.

MyClass* f(...) {

auto_ptr<MyClass> p(new MyClass()); ... return p.release();}

If an exception occurs prior to return, the instance of MyClass will be automatically deleted by p’s destructor.

After auto_ptr is copied (to another one) it points nowhere.