C++ exception syntax

In C++, an exceptional situation is generated by throwing an exception object. That exception object can be anything you like: an object, a pointer, or a built-in type, but because exceptions may be handled by code written by other people it is best to standardize the objects that are used to represent exceptions. For this, the Standard Library provides the exception class, which can be used as a base class.

    double reciprocal(double d) 
{
if (d == 0)
{
// throw 0;
// throw "divide by zero";
// throw new exception("divide by zero");
throw exception("divide by zero");
}
return 1.0 / d;
}

This code tests the parameter and if it is zero then it throws an exception. Four examples are given and all are valid C++, but only the last version is acceptable because it uses a Standard Library class (or one derived from the Standard Library classes) and it follows the convention that exceptions are thrown by value.

When an exception is thrown, the exception handling infrastructure takes over. Execution will stop in the current code block and the exception will be propagated up the call stack. As the exception propagates through a code block, all the automatic objects will be destroyed, but objects created on the heap in the code black will not be destroyed. This is a process called stack unwinding, whereby each stack frame is cleaned up as much as possible before the exception moves to the stack frame above it in the call stack. If the exception is not caught, it will propagate up to the main function, at which point the terminate function will be called to handle the exception (and hence it will terminate the process).

You can protect code to handle propagated exceptions. Code is protected with a try block and it is caught with an associated catch block:

    try  
{
string s("this is an object");
vector<int> v = { 1, 0, -1};
reciprocal(v[0]);
reciprocal(v[1]);
reciprocal(v[2]);
}
catch(exception& e)
{
cout << e.what() << endl;
}

Unlike other code blocks in C++, braces are mandatory even if the try and catch blocks contain single lines of code. In the preceding code the second call to the reciprocal function will throw an exception. The exception will halt the execution of any more code in the block, so the third call to the reciprocal function will not occur. Instead, the exception propagates out of the code block. The try block is the scope of the objects defined between the braces, and this means that the destructors of these objects will be called (s and v). Control is then passed to the associated catch blocks, and in this case, there is just one handler. The catch block is a separate block to the try block, so you cannot access any variables defined in the try block. This makes sense because when an exception is generated the entire code block is tainted so you cannot trust any object created in that block. This code uses the accepted convention, that is, exceptions are caught by reference, so that the actual exception object, and not a copy, is caught.

The convention is: throw my value, catch-by-reference.

The Standard Library provides a function called uncaught_exception, which returns true if an exception has been thrown but not yet handled. It may seem odd to be able to test for this since no code other than the exception infrastructure will be called when an exception has occurred (for example the catch handlers) and you should put exception code there. However, there is other code that is called when an exception is thrown: the destructors of automatic objects that are destroyed during the stack clear up. The uncaught_exception function should be used in a destructor to determine if the object is being destroyed due to an exception rather than normal object destruction due to an object going out of scope or being deleted. For example:

    class test 
{
string str;
public:
test() : str("") {}
test(const string& s) : str(s) {}
~test()
{
cout << boolalpha << str << " uncaught exception = "
<< uncaught_exception() << endl;
}
};

This simple object indicates if it is being destroyed because of exception stack unwinding. It can be tested like this:

    void f(bool b) 
{
test t("auto f");
cout << (b ? "f throwing exception" : "f running fine")
<< endl;
if (b) throw exception("f failed");
}

int main()
{
test t1("auto main");
try
{
test t2("in try in main");
f(false);
f(true);
cout << "this will never be printed";
}
catch (exception& e)
{
cout << e.what() << endl;
}
return 0;
}

The f function will throw an exception only if it is called with a true value. The main function calls f twice, once with a value of false (so the exception is not thrown in f) and a second time with true. The output is:

    f running fine
auto f uncaught exception = false
f throwing exception
auto f uncaught exception = true
in try in main uncaught exception = true
f failed
auto main uncaught exception = false

The first-time f is called, the test object is destroyed normally, so uncaught_exception will return false. The second-time f is called the test object in the function is being destroyed before the exception has been caught, so uncaught_exception will return true. Since an exception is thrown, the execution leaves the try block and so the test object in the try block is destroyed and uncaught_exception will return true. Finally, when the exception has been handled and control returns to code after the catch block, the test object created on the stack in the main function will be destroyed when the main function returns and so uncaught_exception will return false.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset