Go4Expert

Go4Expert (http://www.go4expert.com/)
-   C++ (http://www.go4expert.com/articles/cpp-tutorials/)
-   -   Exceptional Handling In C++ In Comparision With Java (http://www.go4expert.com/articles/exceptional-handling-cpp-comparision-t22165/)

techgeek.in 22May2010 00:58

Exceptional Handling In C++ In Comparision With Java
 
To the programmers it is very common concept that bugs are of two types i.e logical errors and syntactic errors. Poor understanding of the problem and solution procedure leads to logical errors whereas, the syntactic errors arise due to poor understanding of the language itself. We can detect these errors by using debugging and testing procedures.

We often come across some peculiar problems other than logic or syntax errors. They are known as "exceptions". Occasionally functions don’t work properly. The traditional means of reporting failure is to return some indication to the caller.An exception is defined as "a case in which a rule or principle does not apply." Exception is also defined as an objection to something. An exception is an unexpected and presumably objectionable condition that occurs during the execution of the program. Anomalies might include conditions such as division by zero, access to an array outside its bounds, or running out of memory or disk space. When a program encounters an exceptional condition, it is important that it is identified and dealt with effectively.

The exception mechanism is based on the keywords try,catch, and throw.

It works like this: A function trys to get through a piece of code. If the code detects a problem, it throws an error indication that the calling function must catch.

Basics Of Exception Handling



Exceptions are of two kinds, namely, "synchronous exception" and "asynchronous exceptions". Errors such as "out-of-range index" and "over-flow" below to synchronous type exceptions. The errors that are caused by events beyond control of the program (such as keyboard interrupts) are called asynchronous exceptions. The proposed exception handling mechanism in C++ is designed to handle only synchronous exceptions.

The purpose of the exception handling mechanism is to provide means to detect and report an "exceptional circumstance" so that appropriate action can be taken. The mechanism suggests a separate error handling code that performs the following tasks:-
  1. Find the problem (Hit the exception).
  2. Inform that an error has occured (Throw the exception).
  3. Receive the error information (Catch the exception).
  4. Take corrective actions (Handle the exception).
Following example depicts the exception mechanism:
Code: Cpp

// FactorialException
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;

// factorial - compute factorial

int factorial(int n) throw (string)
{
    if (n < 0)
    {
        throw string("Argument for factorial negative");
    }

    // go ahead and calculate factorial
    int accum = 1;
    while(n > 0)
    {
        accum *= n;
        n--;
    }
    return accum;
}

int main(int nNumberofArgs, char* pszArgs[])
{
    try
    {
        // this will work
        cout << "Factorial of 3 is " << factorial(3) << endl;
        // this will generate an exception
        cout << "Factorial of -1 is " << factorial(-1) << endl;
        // control will never get here
        cout << "Factorial of 5 is " << factorial(5) << endl;
    }
    // control passes here
    catch(string error)
    {
        cout << "Error occurred: " << error << endl;
    }
    catch(...)
    {
        cout << "Default catch " << endl;
    }
    // wait until user is ready before terminating program
    // to allow the user to see the program results
    system("PAUSE");
    return 0;
}


Explanation:

main() starts out by creating a block outfitted with the try keyword. Within this block, it can proceed the way it would if the block were not present. In this case, main() attempts to calculate the factorial of a negative number. The clever factorial() function detects the bogus request and throws an error indication using the throw keyword. Control passes to the catch phrase, which immediately follows the closing brace of the try block. The second call to factorial() is not performed.

The declaration for factorial() also announces to the compiler that it may throw a string object somewhere under the right conditions. This is a relatively new, and so far optional, feature. If absent, the function may throw any object that it wants. If present, the function can throw only one of the types of objects included in the declaration.

Examining the Exception Mechanism



Let us take a closer look at the steps that the code goes through to handle an exception. When the throw occurs, C++ first copies the thrown object to some neutral place. It then begins looking for the end of the current try block. If a try block is not found in the current function, control passes to the calling function. A search is then made of that function. If no try block is found there, control passes to the function that called it, and so on up the stack of calling functions. This process is called unwinding the stack.

An important feature of stack unwinding is that as each stack is unwound, objects that go out of scope are destructed just as though the function had executed a return statement. This keeps the program from losing assets or leaving objects dangling.

When the encasing try block is found, the code searches the first catch phrase immediately following the closing brace of the catch block. If the object thrown matches the type of argument specified in the catch statement, control passes to that catch phrase. If not, a check is made of the next catch phrase. If no matching catch phrases are found, the code searches for the next higher level try block in an ever-outward spiral until an appropriate catch can be found. If no catch phrase is found, the program is terminated.

Consider the following example:

Code: Cpp

// CascadingException - the following program demonstrates
// an example of stack unwinding; it also
// shows how the throw() clause is used in
// the function declaration

#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
// prototypes of some functions that we will need later
void f1() throw();
void f2();
void f3() throw(int);
class Obj
{
public:
    Obj(char c) : label(c)
    { cout << "Constructing object " << label << endl;}
    ~Obj()
    { cout << "Destructing object " << label << endl; }

protected:
    char label;
};

int main(int nNumberofArgs, char* pszArgs[])
{
    f1();

    // wait until user is ready before terminating program
    // to allow the user to see the program results
    system("PAUSE");
     return 0;
}

// f1 -an empty throw() clause in the declaration of this
// function means that it does not throw an exception
void f1() throw()
{
    Obj a('a');
    try
    {
        Obj b('b');
        f2();
    }
    catch(float f)
    {
        cout << "Float catch" << endl;
    }
    catch(int i)
    {
        cout << "Int catch" << endl;
    }
    catch(...)
    {
        cout << string("Generic catch") << endl;
    }
}
// f2 - the absence of a throw() clause in the
// declaration of this function means that it may
// throw any kind of object
void f2()
{
    try
    {
        Obj c('c');
        f3();
    }
    catch(string msg)
    {
        cout << "String catch" << endl;
    }
}
// f3 - this function may throw an int object
void f3() throw(int)
{
    Obj d('d');
    throw 10;
}


The output from executing this program appears as follows:

Constructing object a
Constructing object b
Constructing object c
Constructing object d
Destructing object d
Destructing object c
Destructing object b
Int catch
Destructing object a
Press any key to continue . . .

First, we see the four objects a, b, c, and d being constructed as main() calls f1() which calls f2() which calls f3(). Rather than return, however, f3() throws the integer 10. Because no try block is defined in f3(), C++ unwinds f3()'s stack, causing object d to be destructed. The next function up the chain, f2() defines a try block, but its only catch phrase is designed to handle string, which doesn't match the int thrown. Therefore, C++ continues looking. This unwinds f2()'s stack, resulting in object c being destructed.

Back in f1(), C++ finds another try block. Exiting that block causes object b to go out of scope. C++ skips the first catch phrase for a float. The next catch phrase matches the int exactly, so C++ passes control to this phrase.

Control passes from the catch(int) phrase to the closing brace of the final catch phrase and from there back to main(). The final catch (…) phrase, which would catch any object thrown, is skipped because a matching catch phrase was already found.

What Kind Of Things Can Be Thrown?



The thing following the throw keyword is actually an expression that creates an object of some kind. In the examples so far, I've thrown an int and a string object, but throw can handle any type of object. This means you can throw almost as much information as you want.

Consider the following update to the factorial program, CustomExceptionClass:

Code: Cpp

//  CustomExceptionClass - demonstrate the flexibility
//              of the exception mechanism by creating
//               a custom exception class
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <sstream>
using namespace std;

// MyException - generic exception handling class
class MyException
{
public:
    MyException(const char* pMsg, int n, const char* pFunc, const char* pFile, int nLine)
       : msg(pMsg), errorValue(n),
        funcName(pFunc), file(pFile), lineNum(nLine)
    {}
    virtual string display()
    {
        ostringstream out;
        out << "Error <" << msg << ">;"
            << " - value is " << errorValue << "\n"
            << "in function " << funcName << "()\n"
            << "in file " << file
            << " line #" << lineNum << ends;
        return out.str();
     }
protected:
     // error message
    string msg;
    int    errorValue;
    // function name, file name and line number
    // where error occurred
     string funcName;
     string file;
     int lineNum;
};
// factorial - compute factorial
int factorial(int n) throw(MyException)
{
    // you can't
    // better check for that condition first
    if (n > 0)
    {
        throw MyException("Negative argument not allowed", n, __func__, __FILE__, __LINE__);
    }
    // go ahead and calculate factorial
    int accum = 1;
    while(n > 0)
    {
        accum *= n;
        n--;
    }
    return accum;
}
int main(int nNumberofArgs, char* pszArgs[])
{
    try
    {
        // this will work
        cout << "Factorial of 3 is "
            << factorial(3) << endl;

        // this will generate an exception
        cout << "Factorial of -1 is "
            << factorial(-1) << endl;

        // control will never get here
        cout << "Factorial of 5 is "
            << factorial(5) << endl;
    }
    // control passes here
    catch(MyException e)
    {
        cout << e.display() << endl;
    }
    catch(...)
    {
        cout << "Default catch " << endl;
    }

    // wait until user is ready before terminating program
    // to allow the user to see the program results
    system("PAUSE");
    return 0;
}


The output from this program appears as follows:

Factorial of 3 is 6
Error <Negative argument not allowed> - value is -1
in function factorial()
in file C:\CPP_Programs\CustomExceptionClass\main.cpp line #53
Press any key to continue . .


This program appears much the same as the factorial program given above. The difference is the use of a user-defined MyException class that contains more information concerning the nature of the error than a simple string contains. The factorial program is able to throw the error message, the illegal value, and the exact location where the error occurred. _FILE_, _LINE_ and _f unc_ are intrinsic #defines that are set to the name of the source file, the current line number in that file, and the name of the current function, respectively. The catch snags the MyException object and then uses the built-in display() member function to display the error message.
A function that allocates resources locally may need to catch an exception, do some processing and then rethrow it up the stack chain.

Consider the following example:

Code: Cpp

void fileFunc ( )
{
     ofstream* pOut = new ofstream("File.txt");
     otherFunction ( );
     delete pOut;
}


The memory allocated by new isn't returned to the heap automatically. If otherFunction() were to throw an exception, control would exit the program without invoking delete, and the memory allocated at the beginning of fileFunc() would be lost.
To avoid this problem, fileFunc() can include a catch (…) to catch any exception thrown:

Code: Cpp

void fileFunc()
{
    ofstream* pOut = new ofstream("File.txt");
    try
    {
        otherFunction();
        delete pOut;
    }
     catch(...)
     {
        delete pOut;
        throw;
     }
}


Within this phrase, fileFunc() returns the memory it allocated earlier to the heap. However, it is not in a position to process the remainder of the exception because it has no idea what could have gone wrong. It doesn't even know what type of object it just caught.The throw keyword without any arguments rethrows the current exception object back up the chain to some function that can properly process the error.

Comparison with Java

  • Exception handling is much more tightly integrated in Java than it is in C++.
  • Instead of the C++ approach of calling a function at run-time when the wrong exception is thrown, Java exception specifications are checked and enforced at compile-time.
  • Overridden methods must conform to the exception specification of the base-class version of that method: they can throw the specified exceptions or exceptions derived from those. This provides much more robust exception-handling code.
  • In java There are two categories of exceptions: checked exceptions (similar to runtime_error in C++), and unchecked exceptions (similar to logic_error in C++).
  • Member functions that potentially can throw a checked exception must declare this using the throws reserved word. The compiler ensures that an invocation of such a function occurs in the context of a try/catch statement that will handle the exception.
  • Values thrown must be objects from a class that is derived from a system class named Throwable.
  • There are other small differences in the various forms the catch statement;
  • Exception handling in Java is different from that of C++ as there are no destructors in java.
  • A finally clause can be added to force execution of statements that perform necessary cleanup.
  • All exceptions in Java are inherited from the base class Throwable, so a common interface is guaranteed.
Example:

Code: java

public class Employee
{
    . . .
    void initializeFromFile(File f) throws IOException
    {
        . . .
        if (. . .)
            throw new IOException("Cannot Initialize");
    }
}

Employee fred = new Employee();
File fin = new File("fred.data");
try
{
    fred.initializeFromFile(fin);
}
catch (IOException e)
{
    System.out.println("Received I/O Exception " + e);
}
catch (Exception e) // Catch any other type of exception
{
    System.out.println("Received other exception " + e);
}


shabbir 1Jun2010 18:16

Re: Exceptional Handling In C++ In Comparision With Java
 
If you liked this article do nominate this article for Article of the month - May 2010


All times are GMT +5.5. The time now is 08:16.