• Nem Talált Eredményt

Exception handling

int main(){

const int maxn = 123;

for (int i = 1; i <= maxn; i++) { if ((i % 7 != 0) && (i % 12 != 0)) continue;

cout<<i<<endl;

} }

It is a bad practice in programming to use often break and continue statements. It has to be always rethought if given program structures can be solved without jump statements. In the preceding example, this can easily be done by inverting the condition of the if statement:

for (int i = 1; i <= maxn; i++) { if ((i % 7 == 0) || (i % 12 == 0)) cout<<i<<endl;

}

4.4. goto statements

To close the present chapter, let's see one jump statement of C++ not presented yet. We should already be careful when using break and continue statements, but this is even truer for goto statements.

goto statements that can be used to jump within functions have a bad reputation because they make program codes less understandable and more unstructured. Not to mention the tragic consequences of jumping into a loop. So, it should be stated that goto statements are to be avoided if a structured and a clearly understandable code is to be written. In most software developer companies, it is even forbidden.

In order to use goto statements, the statement to which we want to jump has to be labeled. A label is an identifier which is separated by a colon from the following statement:

label: statement

The goto statement with which control can be transferred to the line marked with the label above:

goto label;

In some and only justified cases, e.g. jumping out from a very complicated program structure, goto provides a simple alternative.

5. Exception handling

An anomalous state or event that hinders the normal flow of the execution of a program is called an exception.

In C language, errors were handled locally at every eventual anomalous point of a program. In general, this meant printing out an error message ( cerr ) and then interrupting the execution of that program ( exit ()). For example, when all coefficients of a quadratic equation have been read from the keyboard and the quadratic coefficient is 0, then the quadratic formula cannot be applied to solve the equation:

#include <iostream>

#include <cstdlib>

using namespace std;

int main() {

double a, b, c;

cout<<" coefficients (separated from each"

"other by a space) :";

cin >> a >> b >> c;

if (0 == a) {

cerr << "The equation is not quadratic!" << endl;

exit(-1);

}

cout <<a<<"x^2+"<<b<<"x+"<<c<<"=0"<< endl;

}

C++ makes it possible to assign types to exceptions (errors) and to carry out all exception handling tasks at the same place. Exception handling takes place on the basis of the termination model, which means that the execution of the code part (function) that provoked an exception is interrupted when that exception arises.

Standard C++ language contains relatively few integrated exceptions; however, different class libraries prefer exceptions to signal erroneous states. Exceptions can be triggered manually by "throwing" a value of a given type. Exceptions are transferred (thrown) to the nearest exception handlers that either catch or transfer them.

This above mentioned process functions only if the code part susceptible to trigger an error is enclosed in a statement block that attempts (try) to execute that.

So, the three elements that are needed for the

type-• selecting the code part under exception inspection (try-block),

• transferring exceptions (throw),

• catching and handling exceptions (catch).

5.1. The try – catch program structure

In general, a try-catch program structure contains only one try block and any number of catch blocks:

try {

// statements under exception inspection statements

}

catch (exception1 declaration) { // the handler of exception1 statements1

}

catch (exception2 declaration) { // the handler of exception2 statements2

}

catch (...) {

// the handler of any other exception statements3

}

// statements that are executed after successful handling statements4

In case any statement of a try block causes an exception, the execution of statements is interrupted and control is passed to the catch block (that follows that try block) of the given type (if there is one). If there is no handler with the given type, then the exception is transferred to a handler belonging to an external try block. Exceptions without any type match are finally transferred to the execution environment (as unhandled exceptions). This situation is signalled by the following message: "This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.”

The exception declarations that follow the keyword catch can be provided in many ways. These declarations may contain a type , a variable declaration (type identifier) or three dots . The thrown exception can only be accessed by using the second form. The three dots means that that block catches any exception. Since exceptions are identified in the order catch blocks are provided, the three dotted version should be the last in the catch-block list.

As an example, let's transform the error inspection of the introduction part into an exception handling.

#include <iostream>

#include <cstdlib>

using namespace std;

int main() {

try {

double a, b, c;

cout << " coefficients (separated from each"

"other by a space) :";

cin >> a >> b >> c;

if (0 == a) throw false;

cout <<a<<"x^2+"<<b<<"x+"<<c<<"=0"<< endl;

}

catch (bool) {

cerr << "The equation is not quadratic!" << endl;

exit(-1);

}

catch (...) {

cerr << "An error occurred..." << endl;

exit(-1);

} }

5.2. Provoking exceptions - the throw statement

Exceptions have to be handled locally within the given part of a code with the keywords try, throw and catch.

Exception handling only takes place in case the code in the catch block (and the code called from there) is executed. From the selected code portion, a throw statement

throwexpression;

passes control to the handler that corresponds to the type of the expression, which should be provided after the keyword catch.

When an exception is transferred by a throw statement, then the value of the expression given in the statement is copied into the parameter figuring in the header of the catch block, so this value can be processed in that specific handler. For that purpose, the type also has to have an identifier in that catch statement.

If a catch block contains a throw statement, the caught exception can be transferred as another exception, either transformed to another or not. If the exception is not intended to be transformed, the empty throw statement has to be used: throw;

Exception handling can be made more complete by defining exception classes. The header file named exception contains the description of the exception class, which is the base class of different logical and run-time exceptions. The function named what () of these classes returns the string identifying that exception. The following table summarises the exceptions thrown by the classes defined in the C++ standard library. All exceptions have the exception class as their base class.

Exception classes Header file

exception <exception>

bad_alloc <new>

bad_cast <typeinfo>

bad_typeid <typeinfo>

logic_failure <stdexcept>

domain_error <stdexcept>

invalid_argument <stdexcept>

length_error <stdexcept>

out_of_range <stdexcept>

runtime_error <stdexcept>

range_error <stdexcept>

overflow_error <stdexcept>

underflow_error <stdexcept>

ios_base::failure <ios>

bad_exception <exception>

Exception classes make it possible to pass "smart" objects as exceptions instead of simple values. The knowledge necessary for that process is only treated in III. fejezet - Object-oriented programming in C++ of the present book.

A comfortable way of exception handling is when a string is passed as exception. In that way, the users of a computer program will always receive meaningful messages during their work. (String constants will be declared as of type const char *, which will be treated in the next chapter.)

#include <iostream>

#include <cstdlib>

#include <ctime>

using namespace std;

int main() {

int number;

srand(unsigned(time(NULL)));

try {

Functions in C++ have an important role in exception handling. An exception arising in a function is generally processed within that function since related data are only available from there. However, there are some exceptions that indicate an unsuccessful working of the function back to the element that called the latter. Of course, these exceptions are transferred outside the function.

In the header of a function definition, the keyword throw can be used in a specific way, so the type of the exception to be thrown to the handler can also be defined. By default, all exceptions are transferred.

// all exceptions are transferred.

int funct1();

// only exceptions of type char and bool are transferred int funct2() throw(char, bool);

// only exceptions of type bool are transferred int funct3() throw(bool);

// no exceptions are transferred int funct4() throw();

When a program throws an exception that does not have a handler in that program, the execution environment activates the function named terminate (), which makes the program abort ( abort ()). If a function body throws an exception that is not listed in the exceptions of the given function to be thrown, then the unexpected () system call terminates the program. Programmers can intervene in both terminating process by defining their own handlers that can be registered with the help of the functions set_terminate () or set_unexpected () declared in the header file named except.

5.4. Nested exceptions

A try-catch exception handling structure can be placed within another try-block, either directly or indirectly (i.e. in the function called from that try-block).

As an example, a string exception is thrown from an inner try-block. This is then processed in the inner structure and then transferred by using the empty throw; statement. The outer structure treats that exception finally.

#include <iostream>

} exceptions are stored in enumerations. The program can be exited by using the operator x.

#include <iostream>

6. Pointers, references and dynamic memory