Presentation is loading. Please wait.

Presentation is loading. Please wait.

Object Oriented Programming Lect. Dr. Daniel POP Universitatea de Vest din Timişoara Facultatea de Matematică şi Informatică.

Similar presentations


Presentation on theme: "Object Oriented Programming Lect. Dr. Daniel POP Universitatea de Vest din Timişoara Facultatea de Matematică şi Informatică."— Presentation transcript:

1 Object Oriented Programming Lect. Dr. Daniel POP Universitatea de Vest din Timişoara Facultatea de Matematică şi Informatică

2 2Programming IIObject-Oriented Programming Course #8 Agenda Exception Handling Exceptions try-catch-throw mechanism Hierarchies of exceptions Catching exceptions Resource management Exceptions in constructors, destructor and member initialization Exception specifications

3 3Programming IIObject-Oriented Programming Exception DEFINITION [Exception] An exception is a run-time error. Examples: not enough memory, a file cannot be opened, invalid object received for an operation, etc. How to deal with exceptions? terminate the program => not appropriate return a value representing “error” => what is an acceptable error code? It has to be checked by the caller. return a legal value and leave the program in an illegal state => caller has to test an errno state variable call a function supplied to be called in case of “error” => no control over caller’s code Useful, ordinary code is mixed with error-handling code => less readable programs, hard to maintain => programs become “brittle”

4 4Programming IIObject-Oriented Programming Exception handling DEFINITION [Exception handling] Exception handling is a mechanism that allows two separately developed program components to communicate when a program anomaly, called an exception, is encountered during the execution of the program. OOP error handling style provides programmers with a way of handling errors where they are most naturally handled Designed to handle only synchronous exceptions; asynchronous exceptions require fundamentally different approaches. Exception-handling mechanism in C++ is a non-local control structure based on stack unwinding that can be seen as an alternative return mechanism. There are legitimate uses of exception that have nothing to do with errors.

5 5Programming IIObject-Oriented Programming Exception handling mechanism in C++ An exception is an object of some class representing an exception occurrence. 1. Code that detects an error throws an object (exception). 2. The effect of throw is to unwind the stack until a suitable catch is found (in a function that directly/indirectly called the function that threw the exception). 1. Code that handles an error tries to execute an operation (call a function) 2. Catches all the errors that should be handled. Example: void f() { try { // … code that may raise an exception … // other statements… operation(); // other statements… } catch (ExceptionType1 e1) { // handle Exception type1 } catch (ExceptionType2 e2) { // handle Exception type2 } void operation() { // start ‘normal’ execution flow if(some_exceptional_case_occurred) { // throws an exception object throw ExceptionType(); } // continue ‘normal’ execution flow } For more examples, see slides “Exceptions in constructors” or “Resource management (I)”

6 6Programming IIObject-Oriented Programming Grouping of Exception (I) Exceptions fall naturally into families => one can use inheritance to structure exceptions Example: void f() { try { // … using math functions } catch (Overflow) { // handle Overflow exceptions } catch (MathException e) { // handle any Math exceptions that is not Overflow cout << e.getDescription(); // call MathException::getDescription } class MathException { public: virtual string getDescription() { cout << “Math ex”; } }; class Overflow : public MathException { public: string getDescription() { cout << “Overflow ex”; } }; class Underflow : public MathException { string getDescription() { cout << “Underflow ex”; } }; If not used in catch body, giving a name to exception argument in catch branch is not needed.

7 7Programming IIObject-Oriented Programming Grouping of Exception (II) void f() { try { // … using math functions } catch (Overflow e) { // handle Overflow exceptions cout << e.getDescription(); } catch (MathException& re) { // handle any Math exceptions that is not Overflow cout << e.getDescription(); // call appropriate getDescription (e.g. of class Underflow if an underflow exception was thrown) } Exception hierarchy can include multiple inheritance as well Exceptions hierarchies increase the robustness. Remark: only info specific to MathException is available in catch branch, regardless of the “original” type of exception (slicing effect); to avoid this, use reference types (e.g. MathException&)

8 8Programming IIObject-Oriented Programming Catching exceptions Handler H is invoked when: [1] if H is the same type as E [2] if H is an unambiguous public base of E [3] if H and E are pointer types and [1] or [2] holds for the types to which they refer [4] if H is a reference and [1] or [2] holds for the type to which H refers. void f() { try { throw E(); } catch(H) { } const can be used to denote exceptions that are not modified Example: catch(const MathException& e) { /* */ } In principle, exceptions are copied when it is thrown so that the handler gets a copy of the original. Re-throw an exception void f() { try { } catch(MathException& e) { if(cannot_handle_it_completely) throw; // re-throw an exception else // do the job & consume the exc. } REMARKS Order of handlers is important. Why? catch(…) – catches any exception because ellipsis indicates ‘any argument’

9 9Programming IIObject-Oriented Programming Resource management (I) void use_file(const char* fn) { FILE* fp = fopen(fn, “w”); // use fp fclose(fp); } Solution 1: void use_file(const char* fn) { FILE* fp; try { fp = fopen(fn, “w”); // use fp } catch(…) { fclose(fp); throw; } fclose(fp); } Resource = file, memory, lock Ensure proper release of resource!.... Certainly, not in that example ;) Problems with solution 1: tedious verbose potentially expensive error-prone Fortunately, a more ‘elegant’ and safe solution exists.

10 10Programming IIObject-Oriented Programming Resource management (II) Solution 2 = Resource acquisition is initialization (usage of local objects to allocate/release resources in constructors/destructors). Example: void use_file(const char* fn) { File_ptr f(fn, “w”); // use f fwrite(f, “Text”, 4); // call operator FILE* to convert File_ptr to FILE* } class File_ptr { public: File_ptr(const char* fn, const char* a) { p = fopen(fn, a); } File_ptr(FILE* pp) : p(pp) { } ~File_ptr() { fclose(p); } operator FILE*() { return p; } private: FILE* p; }; Advantages of the 2 nd solution: the destructor will be called independently of whether normal or with-exception function exits simpler main code and less error-prone

11 11Programming IIObject-Oriented Programming Resource management (III) Applying r esource acquisition is initialization to object’s members => assures transactional creation of objects, i.e. either an object is completely created or not at all. Example: class X { public: X(const char* x, const char* y) : f(x, “rw”), // acquire f lck(y) { // acquire lck } private: File_ptr f; Lock_ptr lck; }; “Transactioning” memory allocation class Y { public: Y(int size) { p = new int [size]; init(); } ~Y() { delete [] p; } private: int* p; void init(); }; class YY { public: YY(int size) : p(size) { init(); } private: vector p; void init(); }; Q: Exceptions and new operator. What happens if X’s constructors throws an exception? Is the memory freed? A: In the ordinary case, yes! But, if placement syntax is used the answer depends on the used allocator. Example: X* px = new (a) X [10]; // allocation from allocator a (deallocate it from a); depends on allocator // Allocator’s operator delete function is called.

12 12Programming IIObject-Oriented Programming Resource management (IV) Resource exhausting: when a resource acquisition fails Example: not enough memory Solutions: Resumption – ask the caller to fix the problem and carry on Termination – abandon the computation and return to some caller Resumption – is implemented using function-call mechanism Termination – is implemented using exception-handling mechanism Example: void* operator new(size_t size) { for(;;) { if(void* p = malloc(size)) return p; if(_new_handler==0) throw bad_alloc(); // termination _new_handler(); // resumption } What information does the code causing a resource exhausting send to the caller? More information => increase dependency between each other. To preserve code reusability and to increase maintainability the coupling between different modules/components should be minimized, but it must provide enough information so that the caller can recover from problem reliable and conveniently.

13 13Programming IIObject-Oriented Programming Exceptions in constructors Problem: how to report errors from constructors? Exceptions is an elegant mechanism for this. Example class Vector { static const int MAX_SIZE = 1000; public: class BadSize { } ; Vector(int sz) { if(sz MAX_SIZE) throw BadSize(); // do the actual job } }; void f(int size) { try { Vector v(size); cout << “Going on…\n”; } catch(Vector::BadSize& bs) { cout << “Invalid size “ << size << endl; } cout << “Return.\n”; } int main() { f(-10); // Exception is thrown f(5); // OK return 0; }...and the output is: Invalid size -10 Return. Going on... Return.

14 14Programming IIObject-Oriented Programming Exceptions in members initialization Problem: what happens if a member initialization throws an exception? The constructor can catch this kind of problems by using try-catch block in its initialization list. Example class X { Vector v; public: X(int size); }; X::X(int size) try : v(size) // initialize v by size { } catch(Vector::BadSize) { } { } Copy-constructors and assignment operators are special case of constructors/operators because they are invoked automatically, they deal with acquiring + releasing of resources

15 15Programming IIObject-Oriented Programming Exceptions in destructors A destructor can be called: ― [1] in ‘normal’ way, when objects are destroyed ― [2] during exception handling, when during stack unwinding a scope containing an object with destructor is exited Use uncaught_exception() function to decide whether the destructor was called due to case [1] (returns false) or [2] (returns true). In the later case, if an exception “escapes” from the destructor then std::terminate() function is called to signal an abnormal program termination. To protect itself from this kind of disaster, a destructor can use try- catch block. Example: X::~X() { try { // do the task that might raise an exception } catch(…) { // handle any exception here } void f() { try { X anXObject; // some operations that may generate an Exception } // => destroy anXObject, using X::~X (), in case [2] catch(Exception& e) { } X anotherXObject; } // => destroy anotherXObject, using X::~X(), in case [1]

16 16Programming IIObject-Oriented Programming Exceptions that are not exceptions There’s no magic with exceptions; Exceptions are just another mechanism for execution control. Example: void f(Queue& q) { try { for(;;) { X m = q.get(); // throws Empty if queue is empty // use m } catch(Queue::Empty) { return; } Not clear code, difficult to maintain, less efficient Yet, possible ;) void f(Queue& q) { for(; !q.isEmpty(); X m = q.get()) // use m }

17 17Programming IIObject-Oriented Programming Exception specifications Specify the set of exceptions that might be thrown as part of function declaration. Syntax: type name(arg_list) throw (Exceptions_list); Example: void add(Matrix& m1, Matrix& m2) throw (MathException, bad_alloc); If exception list is missing then the function can throw any exception. void f() throw(); - doesn’t throw any exception REMARKS Exception specifications must be included in both function’s declaration and definition. A virtual function can be overridden only by a function that has an exception-specifications at least as restrictive as its own. If other exception than the ones specified in exception specification is thrown => call to std::unexpected(), which – by default – calls std::terminate(), which calls abort(). User-defined handlers for std::unexpected, std::terminate using set_unexpected, respectively set_terminate functions provided in standard library.

18 18Programming IIObject-Oriented Programming Uncaught exceptions? If an exception is thrown but not caught, the function std::terminate will be called. To handle all the exceptions that can be thrown in a program, main should read like below: int main(int, char*[]) { try { // do the actual job } catch(…) { // handle any uncaught exception so far } Dynamic (run-time) detection of exceptions. Exception handling can be implemented so that there is no run-time overhead when no exception is thrown and throwing an exception is not all that expensive as compared to calling a function. C++ Standard Library exposes a hierarchy of predefined exceptions: bad_alloc, bad_cast, bad_exception, overflow_error etc., all of them derived from class exception.

19 19Programming IIObject-Oriented Programming Further Reading [Stroustrup, 1997] Bjarne Stroustrup – The C++ Programming Language 3rd Edition, Addison Wesley, 1997 [Chapter 14] [Stroustrup, 1997] Bjarne Stroustrup – The C++ Programming Language 3rd Edition, Addison Wesley, 1997 [Chapter 14]


Download ppt "Object Oriented Programming Lect. Dr. Daniel POP Universitatea de Vest din Timişoara Facultatea de Matematică şi Informatică."

Similar presentations


Ads by Google