OOP C12 Exceptions
Intro
Runtime Error
The basic philosophy of C++ is
Badly formed codes will not be run.
- There’s always something happening in run-time and
- It is very important to deal with all possible situation in the future running.
Example: Read files
Briefly reading a file means
- open the file;
- determine its size;
- allocate that much memory;
- read the file into memory;
- close the file;
There maybe many kinds of errors during the whole process. If we mark every kind of error an error code, the code may be complicated.
1 |
|
With exception,
1 |
|
At the point where the problem occurs, you
- might not know what to do with it;
- but do know that you must stop. And somebody, somewhere, must figure out what to do.
exception clean up error handling code: separates
- the codes that describes what you want to do
- from the codes that is executed.
Example: Invalid Index
In vector
,
1 |
|
What should the []
operator do if the index is not valid?
method | notes | |
---|---|---|
1 | Return random memory object | may be invalid |
2 | Return a special error value | for code like x = v[2] + v[4] , the operator will continue to fail |
3 | Just exit | |
4 | Die gracefully (with autopsy!) | like assert |
Raise an exception
Class to represent the error
1 |
|
Then the error could be thrown to mark the error.
1 |
|
Caller Handling levels
-
Don’t Care: If code never even suspects a problem, then code never gets there.
1
2
3
4
5
6
7int func() {
Vector<int> v(12);
v[3] = 5;
int i = v[42]; // out of range and die
// control never gets here!
return i * 5;
} -
Mildly interested: warning but let the exception propagate.
1
2
3
4
5
6
7
8
9void outer2() {
String err("exception caught");
try {
func();
} catch (VectorIndexError) {
cout << err;
throw; // propagate the exception(re-raise)
}
} -
Cares Deeply: use
try
in the outer caller tofunc
:1
2
3
4
5
6
7
8
9
10void outer() {
try {
func();
func2();
}
catch (VectorIndexError& e) {
e.diagnostic(); // This exception does not propagate
}
cout << "Control is here after exception";
} -
Doesn’t care about the particulars:
1
2
3
4
5
6
7void outer3() {
try {
outer2();
} catch (...) { // ... catches ALL exceptions!
cout << "The exception stops here!";
}
}
throw statement raises the exception
- Control propagates back to first handler for that exception (following the call chain)
- Objects on stack are properly destroyed
throw exp;
throws value for matching, while throws;
(valid only within a handler) re-raises the exception being handled.
Try Blocks
the try block takes form of
1 |
|
- Any number of handlers accepted.
- No Handlers, No try block.
- It Costs cycles.
Exception handlers
A handler, taking a single argument (like a formal parameter)
- Selects exception by type
- could re-raise exceptions
1 |
|
Handlers’ Checking
Handlers
are checked in order of appearance.
- Check for exact match
- Apply base class conversions: Reference and pointer types, only
- Ellipsis (…) match all like
catch(...)
Inheritance can be used to structure exceptions!
1
2
3
4
5
6
class MathErr { ...
virtual void diagnostic();
};
class OverflowErr : public MathErr { ... }
class UnderflowErr : public MathErr { ... }
class ZeroDivideErr : public MathErr { ... }The code is
1
2
3
4
5
6
7
8
9
10
11
12
13
try {
// code to exercise math options
throw UnderFlowErr();
}
catch (ZeroDivideErr& e) {
// handle zero divide case
}
catch (MathErr& e) {
// handle other math errors
}
catch (...) {
// any other exceptions
}
Standard library exceptions
new
raises abad_alloc()
exception when failing.
1
2
3
4
5
6
7
8
void func() {
try {
while(1) {
char *p = new char[10000];
}
}
catch (bad_alloc& e) { }
}
Exception specifications
Part of function prototypes
.
1 |
|
Declare which exceptions function might raise. A function with no Exception specifications may throw any type of exception.
- Exceptions are Not checked at compile time.
- If an exception not in the list propagates out at run time, the
unexpected exception
is raised.
More exceptions
Exceptions and constructors
For failures in constructor:
- No return value is possible
- Use an
uninitialized flag
- Defer work to an
Init()
function
Two stages construction strategy
- Do normal work in constructor. Initialize
- all member objects
- all primitive members
- all pointers to 0
- Do addition initialization work in
Init()
function: Those initializations requesting- File
- Network connection
- Memory
- …(any resource)
If the constructor can’t complete, better throw
an exception.
- Destructors for those objects whose constructor didn’t complete won’t be called.
- Therefore Cleaning up allocated resources before throwing is neccesary.
Exceptions and destructors
Destructors are called when
- Normal call: object exits from scope
- During exceptions: stack unwinding invokes dtors on objects as scope is exited.
Design and usage with exceptions
Handlers