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
|
|
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
|
- Create a new C++ Console Application.
- Create a folder called Exceptional2
- Save Unit1 as Main and save the project as Exceptions
- To create a new class, on the main menu, click File -> New... or File -> New -> Other...
- From the New property sheet of the New Object dialog box, double-click Unit
- Save the class as Calculator
- Click the Calculator.h tab and change the content of the file as
follows:
//--------------------------------------------------------------------------- #ifndef CalculatorH #define CalculatorH //--------------------------------------------------------------------------- class TCalculator { public: TCalculator(); TCalculator(char* Oper1, char Opr, char* Oper2); void RequestOperands(); void setOperand1(const char* Oper1) throw(const char*); double getOperand1() const; void setOperand2(const char *Oper2) throw(const char*); double getOperand2() const; void setOperator(const char Opr) throw(const char); char getOperator() const; void setOperation(const char* x, const char p, const char* y); double CalcResult() const; void DisplayResult() const; private: double Operand1; double Operand2; char Operator; }; //--------------------------------------------------------------------------- #endif
- Click the Calculator.cpp tab and change its content as follows:
//--------------------------------------------------------------------------- #include <iostream> using namespace std; #include "Calculator.h" //--------------------------------------------------------------------------- TCalculator::TCalculator() { } //--------------------------------------------------------------------------- TCalculator::TCalculator(char* Oper1, char Opr, char* Oper2) { setOperand1(Oper1); setOperator(Opr); setOperand2(Oper2); } //--------------------------------------------------------------------------- void TCalculator::RequestOperands() { char Number1[40], Number2[40]; char Oper; try { cout << "To proceed, enter\n"; cout << "First Number: "; cin >> Number1; cout << "An Operator: "; cin >> Oper; cout << "Second Number: "; cin >> Number2; setOperand1(Number1); setOperator(Oper); setOperand2(Number2); CalcResult(); } 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"; } catch(const int n) { cout << "\nBad Operation: Division by " << n << " not allowed"; } } //--------------------------------------------------------------------------- void TCalculator::setOperand1(const char* Oper1) throw(const char*) { for(unsigned int i = 0; i < strlen(Oper1); i++) if( (!isdigit(Oper1[i])) && (Oper1[i] != '.') ) throw Oper1; Operand1 = atof(Oper1); } //--------------------------------------------------------------------------- double TCalculator::getOperand1() const { return Operand1; } //--------------------------------------------------------------------------- void TCalculator::setOperand2(const char* Oper2) throw(const char*) { for(unsigned int i = 0; i < strlen(Oper2); i++) if( (!isdigit(Oper2[i])) && (Oper2[i] != '.') ) throw Oper2; Operand2 = atof(Oper2); } //--------------------------------------------------------------------------- double TCalculator::getOperand2() const { return Operand2; } //--------------------------------------------------------------------------- void TCalculator::setOperator(const char Symbol) throw(const char) { if(Symbol != '+' && Symbol != '-' && Symbol != '*' && Symbol != '/') throw Symbol; Operator = Symbol; } //--------------------------------------------------------------------------- char TCalculator::getOperator() const { return Operator; } //--------------------------------------------------------------------------- void TCalculator::setOperation(const char* Oper1, const char Opr, const char* Oper2) { setOperand1(Oper1); setOperator(Opr); setOperand2(Oper2); } //--------------------------------------------------------------------------- double TCalculator::CalcResult() const { double R; // Perform an operation based on the user's choice switch(Operator) { case '+': R = Operand1 + Operand2; break; case '-': R = Operand1 - Operand2; break; case '*': R = Operand1 * Operand2; break; case '/': try { if( Operand2 == 0 ) throw "Division by zero not allowed"; R = Operand1 / Operand2; } catch(const char *Str) { cout << "\nBad Operator: " << Str; } break; } return R; } //--------------------------------------------------------------------------- void TCalculator::DisplayResult() const { double Result; CalcResult(); Result = CalcResult(); // Display the result of the operation cout << "\n" << Operand1 << " " << Operator << " " << Operand2 << " = " << Result; } //---------------------------------------------------------------------------
- Click the Main.cpp tab and change its content as follows:
//--------------------------------------------------------------------------- #include <iostream> using namespace std; #include "Calculator.h" //--------------------------------------------------------------------------- int main(int argc, char* argv[]) { TCalculator Calc; double Number1, Number2, Result; char Oper; cout << "This program allows you to perform an operation on two numbers\n"; Calc.RequestOperands(); Calc.DisplayResult(); return 0; } //---------------------------------------------------------------------------
- Test the program
No comments:
Post a Comment