Facing an Exception
|
An exception is a behavior that should not occur in your program
but is likely to show up. The simplest exception looks like a
conditional statement and here is
an example:
|
#include <iostream> using namespace std; int main() { int StudentAge; cout << "Student Age: "; cin >> StudentAge; try { if(StudentAge < 0) throw; cout << "\nStudent Age: " << StudentAge << "\n\n"; } catch(...) { } cout << "\n"; return 0; } |
If you run this program and type a positive integer for the
student’s age, the program would respond by displaying the student age.
That’s a good outcome.
If you run the program and type a letter
or any character, the compiler would display the student age as 0. This is the first proof that
the compilers are already configured to deal with some abnormal behavior of a program.
When the throw keyword is written by itself, it is a way
of asking the compiler to send the exception to another handler. In
fact, if there is no other handler written by you, the processing would
be handed to the operating system. In this case, if you run the program
and type a negative integer, since the program is not prepared to handle
the exception itself, because of the presence of a single
throw, the operating system would take over and display its own message. This would be “abnormal program termination”
on a Microsoft Windows operating system.
|
#include <iostream> using namespace std; int main() { double Number1, Number2, Result; // Request two numbers from the user cout << "Please provide two numbers\n"; try { cout << "First Number: "; cin >> Number1; cout << "Second Number: "; cin >> Number2; if( Number2 == 0 ) throw; // Perform a division and display the result Result = Number1 / Number2; cout << "\n" << Number1 << " / " << Number2 << " = " << Result << "\n\n"; } catch(...) { } return 0; } |
Test the program and supply two valid numbers such as 126.45 and
5.52
Return to your programming environment and test the program again.
This time, type 0 for the second number. |
Predicting Exceptions
|
Writing Local Exceptions
|
Imagine you write a program that requests a student’s age from the
user. As we know, everybody’s age is positive. Therefore, we need to
figure out what to do if the user types a negative number. The
expression that checks whether the number entered is positive can be
written as:
|
if(StudentAge < 0) |
If the condition is true, the minimum you can do is to send the produced error away. This is done with the
throw keyword:
|
try { if(StudentAge < 0) throw; }
Whenever an exception occurs, and whenever you use the try keyword to try an expression, you must transfer control to a
catch block. This is where you should display your own message for the error. Here is an example:
|
#include <iostream.h>
int main()
{
int StudentAge;
try {
cout << "Student Age: ";
cin >> StudentAge;
if(StudentAge < 0)
throw "Positive Number Required";
cout << "\nStudent Age: " << StudentAge << "\n\n";
}
catch(const char* Message)
{
cout << "Error: " << Message;
}
cout << "\n";
return 0;
}
This program starts with the try block that asks the user to enter a positive number. If the user enters an invalid value, the program examines the
throw keyword. This throw appears to display a string. The compiler registers this string and since there was an exception, the program exits the
try block (it gets out of the try block even if the rest of the
try block is fine) and looks for the first catch block it can find. If it finds a
catch that doesn’t take an argument, it would still use the catch. Otherwise, you can use the
catch block to display the error string that was sent by the throw keyword. In the example above, the
catch uses a string as a pseudo-argument and displays it using a cout extractor.
In the example above, the catch block
is configured to display a string. Let’s consider the classic division
by zero operation. The division by zero is dealt with at different
levels. The processor (Intel, AMD, etc) is configured not to allow it.
The operating system is also prepared for it. Finally, the compiler has
its own interpretation of this operation. Nevertheless, if you suspect
it to occur in your program, you can take appropriate measures. When
preparing to deal with division by zero, the main idea is to compare the
denominator with 0. This comparison should be performed in a
try block. If the comparison renders true, you should avoid the operation and hand the error (exception) to a
catch. The catch is usually used to display a message as in the last code. Here is an example:
|
#include <iostream> using namespace std; int main() { double Operand1, Operand2, Result; // Request two numbers from the user cout << "This program allows you to perform a division of two numbers\n"; cout << "To proceed, enter two numbers: "; try { cout << "First Number: "; cin >> Operand1; cout << "Second Number: "; cin >> Operand2; // Find out if the denominator is 0 if( Operand2 == 0 ) throw "Division by zero not allowed"; // Perform a division and display the result Result = Operand1 / Operand2; cout << "\n" << Operand1 << " / " << Operand2 << " = " << Result << "\n\n"; } catch(const char* Str) // Catch an exception { // Display a string message accordingly cout << "\nBad Operator: " << Str; } return 0; }
The catch clause can use any type of variable as long as
you configure it accordingly. Instead of a string as we have seen, you
can send it an integer, then display an error depending on the integer
that was sent.
|
#include <iostream.h> int main() { double Operand1, Operand2, Result; const char Operator = '/'; // Request two numbers from the user cout << "This program allows you to perform a division of two numbers\n"; cout << "To proceed, enter two numbers\n"; try { cout << "First Number: "; cin >> Operand1; cout << "Second Number: "; cin >> Operand2; // Find out if the denominator is 0 if( Operand2 == 0 ) throw 0; // Perform a division and display the result Result = Operand1 / Operand2; cout << "\n" << Operand1 << " / " << Operand2 << " = " << Result << "\n\n"; } catch(const int n) // Catch an exception { // Display a string message accordingly cout << "\nBad Operator: Division by " << n << " not allowed\n\n"; } return 0; } |
Catching Multiple Exceptions
|
The exceptions as we have seen so far dealt with a single exception in a program. Most of the time, a typical program will
throw different types of errors. The C++ language allows you to include different
catch blocks. Each catch block can face a specific error. The syntax used is:
|
try { Code to Try } catch(Arg1) { One Exception } catch(Arg2) { Another Exception }
The compiler would proceed in a top-down as follows:
Multiple catches are written if or when a try block is expected to
throw different types of errors. Imagine a program that requests some
numbers from the user and performs some operation on the numbers. Such a
program can be written as follows:
|
#include <iostream.h> int main() { double Operand1, Operand2, Result; char Operator; cout << "This program allows you to perform an operation on two numbers\n"; cout << "To proceed, enter a number, an operator, and a number:\n"; cin >> Operand1 >> Operator >> Operand2; switch(Operator) { case '+': Result = Operand1 + Operand2; break; case '-': Result = Operand1 - Operand2; break; case '*': Result = Operand1 * Operand2; break; case '/': Result = Operand1 / Operand2; break; default: cout << "Bad Operation"; } cout << "\n" << Operand1 << " " << Operator << " " << Operand2 << " = " << Result; cout << "\n\n"; return 0; }
This program works fine as long as the user types a valid sequence
of values made of a number followed by a valid arithmetic operator,
followed by a number. Anything else, such
as an invalid number, an unexpected operator, or a wrong sequence
(such as Number Number Operator), would produce an unpredictable
outcome. Obviously various bad things could happen when this program is
running.
To handle the exceptions that this program could produce, you can
start with the most likely problem that would occur. Trusting that a
user is able to provide the two numbers that are requested, it is
possible that
she would type an invalid operator. For example, for this program
we will perform only the addition (+), the subtraction(-), the
multiplication(*), and the division(/). Therefore, we will first
validate the operator. This can be done as follows:
|
#include <iostream> #include <string> using namespace std; int main() { double Operand1, Operand2, Result; char Operator; cout << "This program allows you to perform an operation on two numbers\n"; try { cout << "To proceed, enter a number, an operator, and a number:\n"; cin >> Operand1 >> Operator >> Operand2; if(Operator != '+' && Operator != '-' && Operator != '*' && Operator != '/') throw Operator; switch(Operator) { case '+': Result = Operand1 + Operand2; break; case '-': Result = Operand1 - Operand2; break; case '*': Result = Operand1 * Operand2; break; case '/': Result = Operand1 / Operand2; break; } cout << "\n" << Operand1 << " " << Operator << " " << Operand2 << " = " << Result; } catch(const char n) { cout << "\nOperation Error: " << n << " is not a valid operator"; } cout << "\n\n"; return 0; }
When this program runs, if the user provides two valid numbers but
a wrong operator, the program asks a throw to send a character (in fact
the character that was typed as the operator) that represents the
error. Then, when the compiler gets out of the try block, it looks for
and finds a catch clause that receives a character value. Therefore,
this catch is executed.
Imagine that the user wants to perform a division. You need to
tell the compiler what to do if the user enters the denominator as 0 (or
0.00). If this happens, the best option, and probably the only one you
should consider, is to display a message and get out. To implement this
behavior, we will add another catch block that displays a
message:
|
#include <iostream.h> int main() { double Operand1, Operand2, Result; char Operator; // Request two numbers from the user cout << "This program allows you to perform a division of two numbers\n"; cout << "To proceed, enter two numbers\n"; try { cout << "First Number: "; cin >> Operand1; cout << "Operator: "; cin >> Operator; cout << "Second Number: "; cin >> Operand2; // Make sure the user typed a valid operator if(Operator != '+' && Operator != '-' && Operator != '*' && Operator != '/') throw Operator; // Find out if the denominator is 0 if(Operator == '/') if(Operand2 == 0) throw 0; // Perform an operation based on the user's choice switch(Operator) { case '+': Result = Operand1 + Operand2; break; case '-': Result = Operand1 - Operand2; break; case '*': Result = Operand1 * Operand2; break; case '/': Result = Operand1 / Operand2; break; } // Display the result of the operation cout << "\n" << Operand1 << " " << Operator << " " << Operand2 << " = " << Result << "\n\n"; } catch(const char n) { cout << "\nOperation Error: " << n << " is not a valid operator\n\n"; } catch(const int p) { cout << "\nBad Operation: Division by " << p << " not allowed\n\n"; } return 0; }
When running this program, if the user types a wrong operator, the compiler considers the integer error, gets out f the
try block, and looks for a catch that can use a character. The first catch can validate it and gets executed.
If the user enters the right values
(Number Operator Number), then the compiler finds out if the operator
entered was a forward slash “/” used to perform a division. If the user
wants to perform a division, the compiler finds out if the second
operand, the denominator, is 0. If it is, the program presents a
throw that sends an integer. Based on this exception, the compiler gets out of the
try block and starts looking for a catch block that can use an integer. The first
catch can’t, it uses a character. Therefore, the compiler looks at the next
catch, if any. Our program provides a second catch that takes an
integer as an argument. Therefore, this catch gets executed.
Not all our problems are solved. Image the
user types an invalid number. The first characteristic of an invalid
number is one that contains anything else than a digit (a character
between 0 and 9). Since our program performs its operations on decimal
numbers, we need to allow the number to have a decimal portion. Instead
of expecting numeric values from the user, we will request arrays of
characters. We will use the
isdigit() function to examine each character entered in
order to find out whether any one of them is not a digit. Also, we will
allow the user to type a period that separates the decimal part of a
number. If any of the characters that the user entered is not a digit,
we will
throw a string error
so a catch can deal with it. This means that we will add a catch that is different from the other already existing ones.
So far, we were requesting two
double-precision numbers from the user. In order to check each number
and validate it, instead of decimals, we will ask the user to type two
strings (arrays of
characters):
|
#include <iostream> #include <string> using namespace std; int main() { char Number1[40], Number2[40]; double Operand1, Operand2, Result; char Operator; // Request two numbers from the user cout << "This program allows you to perform a division of two numbers\n"; cout << "To proceed, enter two numbers\n"; try { cout << "First Number: "; cin >> Number1; cout << "Operator: "; cin >> Operator; cout << "Second Number: "; cin >> Number2; // Examine each character of the first operand // to find out if the user included a non-digit in the number for(int i = 0; i < strlen(Number1); i++) if( (!isdigit(Number1[i])) && (Number1[i] != '.') ) // Allow the period throw Number1;// Send the error as a string Operand1 = atof(Number1); // Do the same for the second number entered for(int j = 0; j < strlen(Number2); j++) if( (!isdigit(Number2[j])) && (Number2[j] != '.') ) // Allow the period throw Number2;// Send the error as a string Operand2 = atof(Number2); // Make sure the user typed a valid operator if(Operator != '+' && Operator != '-' && Operator != '*' && Operator != '/') throw Operator; // Find out if the denominator is 0 if(Operator == '/') if(Operand2 == 0) throw 0; // Perform an operation based on the user's choice switch(Operator) { case '+': Result = Operand1 + Operand2; break; case '-': Result = Operand1 - Operand2; break; case '*': Result = Operand1 * Operand2; break; case '/': Result = Operand1 / Operand2; break; } // Display the result of the operation cout << "\n" << Operand1 << " " << Operator << " " << Operand2 << " = " << Result << "\n\n"; } catch(const int n) { cout << "\nBad Operation: Division by " << n << " not allowed\n\n"; } catch(const char n) { cout << "\nOperation Error: " << n << " is not a valid operator\n\n"; } catch(const char *BadOperand) { cout << "\nError: " << BadOperand << " is not a valid number\n\n"; } return 0; }
Nesting Exceptions
|
The calculator simulator we have studied so far performs a
division as one of its assignments. We learned that, in order to perform
any operation. The compiler must first make sure that the user has
entered a
valid operator. Provided the operator is one of those we are
expecting, we also asked the compiler to check that valid numbers were
entered. Even
if these two criteria are met, it was possible that the user enter
0 for the denominator.
The block that is used to check for a non-zero denominator depends
on the exception that validates the operators. In other words, before
we check the value of the denominator, we have first made sure that a
valid number (a string that contains only digits and a period) was
entered for the denominator. For this reason, the exception that could
result from a zero denominator depends on the user first entering a
valid number for the denominator.
C++ allows you to nest exceptions, using the
same techniques we applied to nest conditional statements. This means
that you can write an exception that depends on, and is subject to,
another exception. To nest an exception, write a try block in the body
of the parent exception. The nested try block must be followed by its
own
catch(es). To effectively handle the exception, make sure you include an
appropriate throw in the try block. Here is an exception:
|
#include <iostream> #include <string> using namespace std; int main() { char Number1[40], Number2[40]; double Operand1, Operand2, Result; char Operator; cout << "This program allows you to perform an operation on two numbers\n"; try { cout << "To proceed, enter\n"; cout << "First Number: "; cin >> Number1; cout << "An Operator: "; cin >> Operator; cout << "Second Number: "; cin >> Number2; // Examine each character of the first operand // to find out if the user included a non-digit in the number for(int i = 0; i < strlen(Number1); i++) if( (!isdigit(Number1[i])) && (Number1[i] != '.') ) // Allow the period throw Number1; // Send the error as a character Operand1 = atof(Number1); // Do the same for the second number entered for(int j = 0; j < strlen(Number2); j++) if( (!isdigit(Number2[j])) && (Number2[j] != '.') ) // Allow the period throw Number2;//[j]; // Send the error as a character Operand2 = atof(Number2); if(Operator != '+' && Operator != '-' && Operator != '*' && Operator != '/') throw Operator; switch(Operator) { case '+': Result = Operand1 + Operand2; cout << "\n" << Operand1 << " + " << Operand2 << " = " << Result; break; case '-': Result = Operand1 - Operand2; cout << "\n" << Operand1 << " - " << Operand2 << " = " << Result; break; case '*': Result = Operand1 * Operand2; cout << "\n" << Operand1 << " * " << Operand2 << " = " << Result; break; case '/': // The following exception is nested in the previous try try { if(Operand2 == 0) throw "Division by 0 not allowed"; Result = Operand1 / Operand2; cout << "\n" << Operand1 << " / " << Operand2 << " = " << Result; } catch(const char * Str) { cout << "\nBad Operation: " << Str; } break; } } catch(const char n) { cout << "\nOperation Error: " << n << " is not a valid operator"; } catch(const char *BadOperand) { cout << "\nError: " << BadOperand << " is not a valid number"; } cout << "\n\n"; return 0; }
Exceptions and Functions
|
One of the most effective techniques used to deal with code is to
isolate assignments. We have learned this when studying functions. For
example, the switch statement that was performing the operations in the
“normal” version of our program can be written as follows:
|
#include <iostream> using namespace std; int main() { double Operand1, Operand2, Result; char Operator; double Calculator(const double N1, const double N2, const char p); cout << "This program allows you to perform a division of two numbers\n"; cout << "To proceed, enter a number, an operator, and a number:\n"; cin >> Operand1 >> Operator >> Operand2; Result = Calculator(Operand1, Operand2, Operator); cout << "\n" << Operand1 << " " << Operator << " " << Operand2 << " = " << Result; cout << "\n\n"; return 0; } double Calculator(const double Oper1, const double Oper2, const char Symbol) { double Value; switch(Symbol) { case '+': Value = Oper1 + Oper2; break; case '-': Value = Oper1 - Oper2; break; case '*': Value = Oper1 * Oper2; break; case '/': Value = Oper1 / Oper2; break; } return Value; }
You can still use regular functions along with functions that handle exceptions. Here is an example:
|
#include <iostream> #include <string> using namespace std; double Calculator(const double N1, const double N2, const char p); int main() { char Number1[40], Number2[40]; double Operand1, Operand2, Result; char Operator; cout << "This program allows you to perform an operation on two numbers\n"; try { cout << "To proceed, enter\n"; cout << "First Number: "; cin >> Number1; cout << "An Operator: "; cin >> Operator; cout << "Second Number: "; cin >> Number2; // Examine each character of the first operand // to find out if the user included a non-digit in the number for(int i = 0; i < strlen(Number1); i++) if( (!isdigit(Number1[i])) && (Number1[i] != '.') ) // Allow the period throw Number1; // Send the error as a character Operand1 = atof(Number1); // Do the same for the second number entered for(int j = 0; j < strlen(Number2); j++) if( (!isdigit(Number2[j])) && (Number2[j] != '.') ) // Allow the period throw Number2;//[j]; // Send the error as a character Operand2 = atof(Number2); if(Operator != '+' && Operator != '-' && Operator != '*' && Operator != '/') throw Operator; if(Operator == '/') if(Operand2 == 0) throw 0; Result = Calculator(Operand1, Operand2, Operator); cout << "\n" << Operand1 << " " << Operator << " " << Operand2 << " = " << Result; } catch(const int n) { cout << "\nBad Operation: Division by " << n << " not allowed"; } catch(const char n) { cout << "\nOperation Error: " << n << " is not a valid operator"; } catch(const char *BadOperand) { cout << "\nError: " << BadOperand << " is not a valid number"; } cout << "\n\n"; return 0; } double Calculator(const double Oper1, const double Oper2, const char Symbol) { double Value; switch(Symbol) { case '+': Value = Oper1 + Oper2; break; case '-': Value = Oper1 - Oper2; break; case '*': Value = Oper1 * Oper2; break; case '/': Value = Oper1 / Oper2; break; } return Value; }
As done in the main() function, any member function of a program
can take care of its own exceptions that would occur in its body. Here
is an example of an exception handled in a function:
|
#include <iostream> #include <string> using namespace std; int main() { char Number1[40], Number2[40]; double Operand1, Operand2, Result; char Operator; void Calculator(const double N1, const double N2, const char p); cout << "This program allows you to perform an operation on two numbers\n"; try { cout << "To proceed, enter\n"; cout << "First Number: "; cin >> Number1; cout << "An Operator: "; cin >> Operator; cout << "Second Number: "; cin >> Number2; // Examine each character of the first operand // to find out if the user included a non-digit in the number for(int i = 0; i < strlen(Number1); i++) if( (!isdigit(Number1[i])) && (Number1[i] != '.') ) // Allow the period throw Number1; // Send the error as a character Operand1 = atof(Number1); // Do the same for the second number entered for(int j = 0; j < strlen(Number2); j++) if( (!isdigit(Number2[j])) && (Number2[j] != '.') ) // Allow the period throw Number2; // Send the error as a character Operand2 = atof(Number2); if(Operator != '+' && Operator != '-' && Operator != '*' && Operator != '/') throw Operator; Calculator(Operand1, Operand2, Operator); } catch(const char n) { cout << "\nOperation Error: " << n << " is not a valid operator"; } catch(const char *BadOperand) { cout << "\nError: " << BadOperand << " is not a valid number"; } cout << "\n\n"; return 0; } void Calculator(const double Oper1, const double Oper2, const char Symbol) { double Value; switch(Symbol) { case '+': Value = Oper1 + Oper2; cout << "\n" << Oper1 << " + " << Oper2 << " = " << Value; break; case '-': Value = Oper1 - Oper2; cout << "\n" << Oper1 << " - " << Oper2 << " = " << Value; break; case '*': Value = Oper1 * Oper2; cout << "\n" << Oper1 << " * " << Oper2 << " = " << Value; break; case '/': // The following try exception is nested in the previous try try { if(Oper2 == 0) throw "Division by 0 not allowed"; Value = Oper1 / Oper2; cout << "\n" << Oper1 << " / " << Oper2 << " = " << Value; } catch(const char * Str) { cout << "\nBad Operation: " << Str; } break; } }
Isolating assignments and handing them to functions is a complete
and important matter in the area of application programming. Consider a
program that handles a simple exception such as this one:
|
#include <iostream> using namespace std; int main() { double Operand1, Operand2, Result; char Operator = '/'; cout << "This program allows you to perform a division of two numbers\n"; try { cout << "To proceed, enter two numbers: "; cin >> Operand1 >> Operand2; if( Operand2 == 0 ) throw "Division by zero not allowed"; Result = Operand1 / Operand2; cout << "\n" << Operand1 << " / " << Operand2 << " = " << Result; } catch(const char* Str) { cout << "\nBad Operator: " << Str; } cout << "\n\n"; return 0; }
One of the ways you can use functions in exception routines is to
have a central function that receives variables, sends them to an
external function. The external function tests the value of a variable.
If an exception occurs, the external function displays or sends a throw.
This throw can be picked up by the function that sent the variable. If
the throw carries a value such as an integer or a string, the function
that originated the try can hand it to a catch or one of its catches to
handle the exception. Observe the following example that implements this
scenario:
|
#include <iostream> using namespace std; void Division(const double a, const double b); int main() { double Operand1, Operand2; cout << "This program allows you to perform a division of two numbers\n"; // Start an exception try { cout << "To proceed, enter two numbers: "; cin >> Operand1 >> Operand2; // Pass the new values to a function that will analyze them Division(Operand1, Operand2); } catch(const char* Str) { cout << "\nBad Operator: " << Str; } cout << "\n\n"; return 0; } void Division(const double a, const double b) { double Result; // If an exception occurred, if( b == 0 ) // then throw a string to the function caller throw "Division by zero not allowed"; Result = a / b; cout << "\n" << a << " / " << b << " = " << Result; }
In this program, the Division function receives two values that it
is asked to perform and division with. The Division function analyzes
the second argument that represents the denominator. If this argument is
zero, an exception is fund and the Division functions throws a string
back to the function that sent the arguments.
C++ (as described in the C++ Standards), allows you to specify that a function is an exception carrier. If you write a function that carries an exception, you can type the throw keyword followed by parentheses on the right side of the function. Here is an example: |
#include <iostream> using namespace std; void Division(const double a, const double b) throw(); int main() { double Operand1, Operand2; cout << "This program allows you to perform a division of two numbers\n"; // Start an exception try { cout << "To proceed, enter two numbers: "; cin >> Operand1 >> Operand2; // Pass the new values to a function that will analyze them Division(Operand1, Operand2); } catch(const char* Str) { cout << "\nBad Operator: " << Str; } cout << "\n\n"; return 0; } void Division(const double a, const double b) throw() { double Result; // If an exception occurred, if( b == 0 ) // then throw a string to the function caller throw; Result = a / b; cout << "\n" << a << " / " << b << " = " << Result; }
As if it were a function, the throw keyword used like this must
have parentheses. If it doesn’t take any argument, the parentheses must
be left empty as in the last example. If the function that is called
from a try block will throw a specific type of exception, you can
specify this in the parentheses of the throw. Here is an example:
|
#include <iostream> #include <string> using namespace std; void Calculator(const double N1, const double N2, const char p) throw(const char*); int main() { char Number1[40], Number2[40]; double Operand1, Operand2; char Operator; cout << "This program allows you to perform an operation on two numbers\n"; try { cout << "To proceed, enter\n"; cout << "First Number: "; cin >> Number1; cout << "An Operator: "; cin >> Operator; cout << "Second Number: "; cin >> Number2; for(int i = 0; i < strlen(Number1); i++) if( (!isdigit(Number1[i])) && (Number1[i] != '.') ) throw Number1; Operand1 = atof(Number1); for(int j = 0; j < strlen(Number2); j++) if( (!isdigit(Number2[j])) && (Number2[j] != '.') ) throw Number2; Operand2 = atof(Number2); if(Operator != '+' && Operator != '-' && Operator != '*' && Operator != '/') throw Operator; try { Calculator(Operand1, Operand2, Operator); } catch(const char * Str) { cout << "\nBad Operation: " << Str; } } catch(const char n) { cout << "\nOperation Error: " << n << " is not a valid operator"; } catch(const char *BadOperand) { cout << "\nError: " << BadOperand << " is not a valid number"; } cout << "\n\n"; return 0; } void Calculator(const double Oper1, const double Oper2, const char Symbol) throw(const char*) { double Value; switch(Symbol) { case '+': Value = Oper1 + Oper2; cout << "\n" << Oper1 << " + " << Oper2 << " = " << Value; break; case '-': Value = Oper1 - Oper2; cout << "\n" << Oper1 << " - " << Oper2 << " = " << Value; break; case '*': Value = Oper1 * Oper2; cout << "\n" << Oper1 << " * " << Oper2 << " = " << Value; break; case '/': if(Oper2 == 0) throw "Division by 0 not allowed"; Value = Oper1 / Oper2; cout << "\n" << Oper1 << " / " << Oper2 << " = " << Value; break; } }
A function can also be called to perform more than one test to
eventually throw more than one exception. Such a function can (and
should) be programmed to throw different types of exceptions. Here is an
eample of such a function:
|
double Calculator(const double Oper1, const double Oper2, const char Symbol) { double Value; if(Symbol != '+' && Symbol != '-' && Symbol != '*' && Symbol != '/') throw Symbol; switch(Symbol) { case '+': Value = Oper1 + Oper2; cout << "\n" << Oper1 << " + " << Oper2 << " = " << Value; break; case '-': Value = Oper1 - Oper2; cout << "\n" << Oper1 << " - " << Oper2 << " = " << Value; break; case '*': Value = Oper1 * Oper2; cout << "\n" << Oper1 << " * " << Oper2 << " = " << Value; break; case '/': if(Oper2 == 0) throw "Division by 0 not allowed"; Value = Oper1 / Oper2; cout << "\n" << Oper1 << " / " << Oper2 << " = " << Value; break; } return Value; }
As you can see, this function throws two different types of
exceptions: a character and a string. When writing such a function that
throws, but doesn’t handle, different exceptions, you should make sure
this function throws different types of exceptions. Here is the reason.
When a function throws an exception, it only sometimes specifies the
type of exception. It doesn’t specify where the exception is going. When
the function that called this function receives the thrown type, it
must figure out what block must catch the throw. If this function (the
function that was called) throws various exceptions of the same type,
the calling function would send all of them to the same catch. On the
other hand, if the called function throws different types of exceptions,
the calling function, when it receives the throws, can send each to the
appropriate type that would handle it.
When a function throws an exception, we
learned that we can use the throw keyword on the right side of the
function as if it were a function. We also learned to pass an argument
to the throw to specify the type of exception that the called function
would deal with. If a function is programmed to throw different types of
exceptions, you can specify this in the arguments of the throw that is
appended to the function. Here are examples:
|
#include <iostream> #include <string> using namespace std; void Calculator(const double N1, const double N2, const char p) throw(const char*, const char); double Validate(const char *N) throw(const char*); int main() { char Number1[40], Number2[40]; double Operand1, Operand2; char Operator; cout << "This program allows you to perform an operation on two numbers\n"; try { cout << "To proceed, enter\n"; cout << "First Number: "; cin >> Number1; cout << "An Operator: "; cin >> Operator; cout << "Second Number: "; cin >> Number2; Operand1 = Validate(Number1); Operand2 = Validate(Number2); try { Calculator(Operand1, Operand2, Operator); } catch(const char * Str) { cout << "\nBad Operation: " << Str; } } catch(const char n) { cout << "\nOperation Error: " << n << " is not a valid operator"; } catch(const char *BadOperand) { cout << "\nError: " << BadOperand << " is not a valid number"; } cout << "\n\n"; return 0; } void Calculator(const double Oper1, const double Oper2, const char Symbol) throw(const char*, const char) { double Value; if(Symbol != '+' && Symbol != '-' && Symbol != '*' && Symbol != '/') throw Symbol; switch(Symbol) { case '+': Value = Oper1 + Oper2; cout << "\n" << Oper1 << " + " << Oper2 << " = " << Value; break; case '-': Value = Oper1 - Oper2; cout << "\n" << Oper1 << " - " << Oper2 << " = " << Value; break; case '*': Value = Oper1 * Oper2; cout << "\n" << Oper1 << " * " << Oper2 << " = " << Value; break; case '/': if(Oper2 == 0) throw "Division by 0 not allowed"; Value = Oper1 / Oper2; cout << "\n" << Oper1 << " / " << Oper2 << " = " << Value; break; } } double Validate(const char* N) throw(const char*) { double Valid; for(int i = 0; i < strlen(N); i++) if( (!isdigit(N[i])) && (N[i] != '.') ) throw N; Valid = atof(N); return Valid; }
No comments:
Post a Comment