Classes and Exceptions Handling
Classes and Exceptions Handling
Introduction
Exceptions are an integral and unavoidable part of the operating system and programming. One way you can handle them is to create classes whose behaviors are prepared to deal with abnormal behavior. There are two main ways you can involve classes with exception handling routines: classes that are involved in exceptions of their own operations and classes that are specially written to handle exceptions for other classes.
Transferring Exceptions to Classes
You can create a class that is not specifically oriented towards exceptions, as any of the classes we have used so far. The simplest way to take care of exceptions in classes is to use any normal class and handle its exceptions. Such a class appears like one of the classes we have used already, except that exceptions of its abnormal behavior are taken care of. If concerned with exceptions, the minimum thing you can do in your program is to make it "aware' of eventual exceptions. This can be taken care of by including transactions or other valuable processing in a try block, followed by a three-dot catch as in catch(...). The catch in this case is prepared to handle any exception that could occur. Here is an example of a simple class:
//---------------------------------------------------------------------------
#include <iostream>
#include <iomanip>
using namespace std;
//---------------------------------------------------------------------------
const double PriceShirt = 0.99;
const double PricePants = 1.75;
struct TCleaningOrder
{
int NumberOfShirts;
int NumberOfPants;
int NumberOfMisc;
};
int main(int argc, char* argv[])
{
TCleaningOrder Order;
double TotalPriceShirts, TotalPricePants;
double PriceMisc, TotalPriceMisc;
double TotalOrder;
cout << " - Georgetown Cleaning Services -\n";
cout << " - Customer Order Processing -\n";
try {
cout << "Number of\n";
cout << "Shirts: ";
cin >> Order.NumberOfShirts;
cout << "Pairs of Paints: ";
cin >> Order.NumberOfPants;
cout << "Misc. Items(if none, type 0): ";
cin >> Order.NumberMisc;
// If there are miscalleanous items,...
if(Order.NumberOfMisc > 0)
{
// let the user determine the price of this misc item
cout << "Enter the price of each miscellanous item: ";
cin >> PriceMisc;
TotalPriceMisc = Order.NumberOfMisc * PriceMisc;
}
else
TotalPriceMisc = 0.00;
TotalPriceShirts = Order.NumberOfShirts * PriceShirt;
TotalPricePants = Order.NumberOfPants * PricePants;
TotalOrder = TotalPriceShirts + TotalPricePants + TotalPriceMisc;
cout << setiosflags(ios::fixed) << setprecision(2);
cout << " - Georgetown Cleaning Services -";
cout << "\n - Customer Receipt -";
cout << "\n============================";
cout << "\n Item\tNumber\tPrice";
cout << "\n----------------------------";
cout << "\n Shirts\t" << Order.NumberOfShirts
<< "\t$" << TotalPriceShirts;
cout << "\n Pants\t" << Order.NumberOfPants
<< "\t$" << TotalPricePants;
cout << "\n Misc\t" << Order.NumberOfMisc
<< "\t$" << TotalPriceMisc;
cout << "\n============================";
cout << "\n Total Order:\t$" << TotalOrder;
}
catch(...)
{
cout << "\nSomething went wrong - Too Bad";
}
return 0;
}
//---------------------------------------------------------------------------
Nevertheless, we have learned so far to catch exceptions and to throw them to a section that can handle them. One of the problems that could occur in a program is the clerk not entering valid numbers, which would imply that we want the compiler to multiply strings by constant numbers. Therefore, we can examine each number that the clerk would type and handle its exception in case of an invalid number.
The simplest way you can check an integer is through the use of the isdigit() function. The only problem we will have at this time is that the isdigit() function checks only one digit or character. Here is one attempt at addressing the problem:
//---------------------------------------------------------------------------
#include <iostream>
#include <iomanip>
using namespace std;
//---------------------------------------------------------------------------
// Constant prices of items, set by the store management
const double PriceShirt = 0.99;
const double PricePants = 1.75;
// Create an order object
struct TCleaningOrder
{
int NumberOfShirts;
int NumberOfPants;
int NumberOfMisc;
};
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
TCleaningOrder Order;
double TotalPriceShirts, TotalPricePants;
double PriceMisc, TotalPriceMisc;
double TotalOrder;
cout << " - Georgetown Cleaning Services -\n";
cout << " - Customer Order Processing -\n";
try {
cout << "Number of\n";
cout << "Shirts: ";
cin >> Order.NumberOfShirts;
if( isdigit(Order.NumberOfShirts) )
throw Order.NumberOfShirts;
cout << "Pairs of Paints: ";
cin >> Order.NumberOfPants;
if( isdigit(Order.NumberOfPants) )
throw Order.NumberOfPants;
cout << "Misc. Items(if none, type 0): ";
cin >> Order.NumberMisc;
if( isdigit(Order.NumberOfMisc) )
throw Order.NumberOfMisc;
// If there are miscalleanous items,...
if(Order.NumberMisc > 0)
{
// let the user determine the price of this misc item
cout << "Enter the price of each miscellanous item: ";
cin >> PriceMisc;
TotalPriceMisc = Order.NumberOfMisc * PriceMisc;
}
else
TotalPriceMisc = 0.00;
TotalPriceShirts = Order.NumberOfShirts * PriceShirt;
TotalPricePants = Order.NumberOfPants * PricePants;
TotalOrder = TotalPriceShirts + TotalPricePants + TotalPriceMisc;
cout << setiosflags(ios::fixed) << setprecision(2);
cout << " - Georgetown Cleaning Services -";
cout << "\n - Customer Receipt -";
cout << "\n============================";
cout << "\n Item\tNumber\tPrice";
cout << "\n----------------------------";
cout << "\n Shirts\t" << Order.NumberOfShirts
<< "\t$" << TotalPriceShirts;
cout << "\n Pants\t" << Order.NumberOfPants
<< "\t$" << TotalPricePants;
cout << "\n Misc\t" << Order.NumberMisc
<< "\t$" << TotalPriceMisc;
cout << "\n============================";
cout << "\n Total Order:\t$" << TotalOrder;
}
catch(const int n)
{
cout << n << " is not a valid number";
}
catch(...)
{
cout << "\nSomething went wrong - Too Bad";
}
return 0;
}
//---------------------------------------------------------------------------
Practical Learning: Introduction to Class' Exceptions
//---------------------------------------------------------------------------
#include <iostream>
using namespace std;
//---------------------------------------------------------------------------
class TCalculator
{
public:
double Operand1;
double Operand2;
char Operator;
};
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
TCalculator Calc;
double Result;
// Request two numbers from the user
cout << "This program allows you to perform an operation on two numbers\n";
cout << "To proceed, enter two numbers\n";
try {
cout << "First Number: ";
cin >> Calc.Operand1;
cout << "Operator: ";
cin >> Calc.Operator;
cout << "Second Number: ";
cin >> Calc.Operand2;
// Make sure the user typed a valid operator
if(Calc.Operator != '+' && Calc.Operator != '-' &&
Calc.Operator != '*' && Calc.Operator != '/')
throw Calc.Operator;
// Find out if the denominator is 0
if(Calc.Operator == '/')
if(Calc.Operand2 == 0)
throw 0;
// Perform an operation based on the user's choice
switch(Calc.Operator)
{
case '+':
Result = Calc.Operand1 + Calc.Operand2;
break;
case '-':
Result = Calc.Operand1 - Calc.Operand2;
break;
case '*':
Result = Calc.Operand1 * Calc.Operand2;
break;
case '/':
Result = Calc.Operand1 / Calc.Operand2;
break;
}
// Display the result of the operation
cout << "\n" << Calc.Operand1 << " " << Calc.Operator << " "
<< Calc.Operand2 << " = " << Result;
}
catch(const char n)
{
cout << "\nOperation Error: " << n << " is not a valid operator";
}
catch(const int p)
{
cout << "\nBad Operation: Division by " << p << " not allowed";
}
return 0;
}
//---------------------------------------------------------------------------
This program allows you to perform an operation on two numbers To proceed, enter First number: 1450 Second number: 32 1450 / 32 = 45.3125 Press any key to continue... |
//---------------------------------------------------------------------------
#include <iostream>
using namespace std;
//---------------------------------------------------------------------------
class TCalculator
{
public:
double Operand1;
double Operand2;
char Operator;
};
//---------------------------------------------------------------------------
double CalcResult(const double x, const char p, const double y)
{
double R;
// Perform an operation based on the user's choice
switch(p)
{
case '+':
R = x + y;
break;
case '-':
R = x - y;
break;
case '*':
R = x * y;
break;
case '/':
if( y == 0 )
throw 0;
R = x / y;
break;
}
return R;
}
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
TCalculator Calc;
double Result;
// Request two numbers from the user
cout << "This program allows you to perform an operation of two numbers\n";
cout << "To proceed, enter two numbers\n";
try {
cout << "First Number: ";
cin >> Calc.Operand1;
cout << "Operator: ";
cin >> Calc.Operator;
cout << "Second Number: ";
cin >> Calc.Operand2;
// Make sure the user typed a valid operator
if(Calc.Operator != '+' && Calc.Operator != '-' &&
Calc.Operator != '*' && Calc.Operator != '/')
throw Calc.Operator;
Result = CalcResult(Calc.Operand1, Calc.Operator, Calc.Operand2);
// Display the result of the operation
cout << "\n" << Calc.Operand1 << " " << Calc.Operator << " "
<< Calc.Operand2 << " = " << Result << "\n\n";
}
catch(const char n)
{
cout << "\nOperation Error: " << n << " is not a valid operator";
}
catch(const int p)
{
cout << "\nBad Operation: Division by " << p << " not allowed";
}
cout << "\nPress any key to continue...";
return 0;
}
//---------------------------------------------------------------------------
|
|
Introduction to Exceptions in Classes |
|
Probably the most basic use of exceptions you can make of a class is to let a class handle its own exceptions and hand the result, reliable results, to its clients. You handle exceptions in a class by using its method member. The main difference between a regular function and a class’ member function is that a regular function may need arguments from external functions to carry intermediary assignments. A member function of a class can use the member variables of the same class as if they were passed as arguments. You can use this feature of classes to handle exceptions effectively. This allows each class' method to handle its own exception(s), if any. We saw earlier that when an order is being processed, a clerk can enter an invalid number of items. Just like we did in the main() function, we can take care of this possible exception in a function where the order is being processed. Therefore, when solution to implement for this type of scenario is to declare a function that processes orders for the TCleaningOrder object. Here is an example: |
#include <iostream>
#include <iomanip>
using namespace std;
// Constant prices of items, set by the store management
const double PriceShirt = 0.99;
const double PricePants = 1.75;
// Create an order object
struct TCleaningOrder
{
public:
TCleaningOrder();
~TCleaningOrder();
void ProcessOrder();
void DisplayReceipt();
private:
int NumberOfShirts;
int NumberOfPants;
int NumberOfMisc;
double TotalPriceShirts;
double TotalPricePants;
double TotalPriceMisc;
double TotalOrder;
};
//---------------------------------------------------------------------------
TCleaningOrder::TCleaningOrder()
: NumberOfShirts(0), NumberOfPants(0), NumberMisc(0)
{
}
//---------------------------------------------------------------------------
TCleaningOrder::~TCleaningOrder()
{
}
//---------------------------------------------------------------------------
void TCleaningOrder::ProcessOrder()
{
double PriceMisc;
try {
cout << "Number of\n";
cout << "Shirts: ";
cin >> NumberOfShirts;
if( isdigit(NumberOfShirts) )
throw NumberOfShirts;
cout << "Pairs of Paints: ";
cin >> NumberOfPants;
if( isdigit(NumberOfPants) )
throw NumberOfPants;
cout << "Misc. Items(if none, type 0): ";
cin >> NumberMisc;
if( isdigit(NumberOfMisc) )
throw NumberOfMisc;
// If there are miscalleanous items,...
if(NumberMisc > 0)
{
// let the user determine the price of this misc item
cout << "Enter the price of each miscellanous item: ";
cin >> PriceMisc;
TotalPriceMisc = NumberOfMisc * PriceMisc;
}
else
TotalPriceMisc = 0.00;
TotalPriceShirts = NumberOfShirts * PriceShirt;
TotalPricePants = NumberOfPants * PricePants;
TotalOrder = TotalPriceShirts + TotalPricePants + TotalPriceMisc;
}
catch(const int n)
{
cout << n << " is not a valid number";
}
catch(...)
{
cout << "\nSomething went wrong - Too Bad";
}
}
//---------------------------------------------------------------------------
void TCleaningOrder::DisplayReceipt()
{
cout << setiosflags(ios::fixed) << setprecision(2);
cout << " - Georgetown Cleaning Services -";
cout << "\n - Customer Receipt -";
cout << "\n============================";
cout << "\n Item\tNumber\tPrice";
cout << "\n----------------------------";
cout << "\n Shirts\t" << NumberOfShirts
<< "\t$" << TotalPriceShirts;
cout << "\n Pants\t" << NumberOfPants
<< "\t$" << TotalPricePants;
cout << "\n Misc\t" << NumberOfMisc
<< "\t$" << TotalPriceMisc;
cout << "\n============================";
cout << "\n Total Order:\t$" << TotalOrder;
}
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
TCleaningOrder Order;
cout << " - Georgetown Cleaning Services -\n";
cout << " - Customer Order Processing -\n";
Order.ProcessOrder();
clrscr();
Order.DisplayReceipt();
return 0;
}
//---------------------------------------------------------------------------
|
|
Practical Learning: Class' Methods and their Exceptions |
|
|
Exception-Oriented Classes |
|
As in the earlier ProcessOrder() function, a method, and each necessary method, of a class can handle its own exceptions locally, perform the desired assignment, and send the result to the client that made the call. Alternatively, just like our main() function had been previously, you can create a member function of a class and let that function act as the central point of the class. Such a typical function is used to process transactions related to a class. To do this, implement the other member functions and let them throw the exceptions they encounter. While a member function is carrying its assignment, if it encounters an exception, it can throw it to the central function where the call was made. The central function would find the appropriate catch that can handle the exception. This scenario can be applied to set functions because a set function has the responsibility of checking or validating the value carried by its corresponding member variable. If the value is invalid, the set function can simply throw an exception and get out. Such a set function would look like this: |
void TCleaningOrder::setShirts(const int Shirts)
{
if( isdigit(Shirts) )
throw Shirts;
NumberOfShirts = Shirts;
}
|
|
This member function receives an argument and checks it. If the sent argument is not a valid digit, the setShirts() method throws an exception that carries the same argument that was sent. As you can see, and as we have done with throwing exceptions so far, the setShirts() method doesn't care who (that is, what) sent the wrong argument; it simply throws it back and stops there. On the other hand, if the argument that was sent is good, the setShirts() method assigns it to the NumberOfShirts member variable (remember that it is the setShirts() responsibility to control the value that its corresponding member variable, in this case NumberOfShirts, carries). The client function that sent the request (the request consisted of asking the setShirts() function to validate the character) will need to know what to do with the thrown exception. Once a "central" function that passes arguments to other member methods that validate them, this "central" function doesn't need to throw any more exceptions, but since the others will likely or possibly throw exceptions, our "central function needs to be prepared to catch them and handle them appropriately. In the following example, the ProcessOrder() method acts as that "central" function: |
//---------------------------------------------------------------------------
#include <iostream>
#include <iomanip>
using namespace std;
//---------------------------------------------------------------------------
// Constant prices of items, set by the store management
const double PriceShirt = 0.99;
const double PricePants = 1.75;
// Create an order object
struct TCleaningOrder
{
public:
void setShirts(const int Shirts);
int getShirts() { return NumberOfShirts; }
void setPants(const int Pants);
int getPants() { return NumberOfPants; }
void setMisc(const int Misc);
int getMisc() { return NumberOfMisc; }
TCleaningOrder();
~TCleaningOrder();
void ProcessOrder();
void DisplayReceipt();
private:
int NumberOfShirts;
int NumberOfPants;
int NumberOfMisc;
double TotalPriceShirts;
double TotalPricePants;
double TotalPriceMisc;
double TotalOrder;
};
//---------------------------------------------------------------------------
TCleaningOrder::TCleaningOrder()
: NumberOfShirts(0), NumberOfPants(0), NumberOfMisc(0)
{
}
//---------------------------------------------------------------------------
TCleaningOrder::~TCleaningOrder()
{
}
//---------------------------------------------------------------------------
void TCleaningOrder::setShirts(const int Shirts)
{
if( isdigit(Shirts) )
throw Shirts;
NumberOfShirts = Shirts;
}
//---------------------------------------------------------------------------
void TCleaningOrder::setPants(const int Pants)
{
if( isdigit(Pants) )
throw Pants;
NumberOfPants = Pants;
}
//---------------------------------------------------------------------------
void TCleaningOrder::setMisc(const int Misc)
{
if( isdigit(Misc) )
throw Misc;
NumberOfMisc = Misc;
}
//---------------------------------------------------------------------------
void TCleaningOrder::ProcessOrder()
{
double PriceMisc;
int Shirts, Pants, Misc;
try {
cout << "Number of\n";
cout << "Shirts: ";
cin >> Shirts;
setShirts(Shirts);
cout << "Pairs of Paints: ";
cin >> Pants;
setPants(Pants);
cout << "Misc. Items(if none, type 0): ";
cin >> Misc;
setMisc(Misc);
// If there are miscalleanous items,...
if(getMisc() > 0)
{
// let the user determine the price of this misc item
cout << "Enter the price of each miscellanous item: ";
cin >> PriceMisc;
TotalPriceMisc = NumberOfMisc * PriceMisc;
}
else
TotalPriceMisc = 0.00;
TotalPriceShirts = NumberOfShirts * PriceShirt;
TotalPricePants = NumberOfPants * PricePants;
TotalOrder = TotalPriceShirts + TotalPricePants + TotalPriceMisc;
}
catch(const int n)
{
cout << n << " is not a valid number";
}
catch(...)
{
cout << "\nSomething went wrong - Too Bad";
}
}
//---------------------------------------------------------------------------
void TCleaningOrder::DisplayReceipt()
{
cout << setiosflags(ios::fixed) << setprecision(2);
cout << " - Georgetown Cleaning Services -";
cout << "\n - Customer Receipt -";
cout << "\n============================";
cout << "\n Item\tNumber\tPrice";
cout << "\n----------------------------";
cout << "\n Shirts\t" << NumberOfShirts
<< "\t$" << TotalPriceShirts;
cout << "\n Pants\t" << NumberOfPants
<< "\t$" << TotalPricePants;
cout << "\n Misc\t" << NumberOfMisc
<< "\t$" << TotalPriceMisc;
cout << "\n============================";
cout << "\n Total Order:\t$" << TotalOrder;
}
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
TCleaningOrder Order;
cout << " - Georgetown Cleaning Services -\n";
cout << " - Customer Order Processing -\n";
Order.ProcessOrder();
clrscr();
Order.DisplayReceipt();
return 0;
}
//---------------------------------------------------------------------------
|
|
Practical Learning: Improving Class' Exceptions |
|
For our exercise, we will create a member function for a Calculator class. We use this function to proces a calculation. We also prepare this function to handle various exceptions that can be (or would be) thrown by other functions. The exceptions can be thrown based on a wrong operand or a wrong operator. The class can be implemented in a program as follows (some functions, such as the get methods were added to allow external functions to communicate with the member variables of the class). |
|
|
Separating the Object from its Implementation |
|
As we did in previous lessons, you don't need to keep a class and its source file in the same file. You can separate the header and the C++ source in separate files. This is done in the same way you would do by creating a unit. |
|
Practical Learning: Creating a Class |
| ||||||||||||