Exceptions and Classes


 

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.h>
#include <iomanip.h>
#include <conio.h>
#pragma hdrstop

//---------------------------------------------------------------------------

#pragma argsused
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;

        clrscr();
        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";
    }

    cout << "\n\nPress any key to continue...";
    getchar();
    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.h>
#include <iomanip.h>
#include <conio.h>
#pragma hdrstop
//---------------------------------------------------------------------------
#pragma argsused
// 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;

        clrscr();
        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";
    }

    cout << "\n\nPress any key to continue...";
    getchar();
    return 0;
}
//---------------------------------------------------------------------------

 

 

Practical Learning: Introduction to Class' Exceptions

  1. Start Borland C++ Builder and create a C++ Console Application using the Console Wizard.
  2. Click the Save All button on the Standard toolbar.
  3. Create a folder called Exceptional1 and display it in the Save In combo box.
  4. Save the unit as Main and save the project as Exceptional
  5. Change the content of the Main.cpp file as follows:
     
    //---------------------------------------------------------------------------
    #include <iostream.h>
    #pragma hdrstop
    //---------------------------------------------------------------------------
    #pragma argsused
    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";
        }
        
        cout << "\n\nPress any key to continue...";
        getchar();
        return 0;
    }
    //---------------------------------------------------------------------------
  6. To test your program, on the main menu, click Run -> Run
  7. Test the program with two non-zero numbers. Here is an example:
     

    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...

  8. Return to Bcb
  9. You can also use external functions that check and throw exceptions to other functions that can handle them. As an example, change the program as follows:
     
    //---------------------------------------------------------------------------
    #include <iostream.h>
    #pragma hdrstop
    //---------------------------------------------------------------------------
    #pragma argsused
    class TCalculator
    {
    public:
        double Operand1;
        double Operand2;
        char Operator;
    };
    //---------------------------------------------------------------------------
    double __fastcall 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...";
        getchar();
        return 0;
    }
    //---------------------------------------------------------------------------
  10. Test it and return to Bcb.
 

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.h>
#include <iomanip.h>
#include <conio.h>
#pragma hdrstop
//---------------------------------------------------------------------------
#pragma argsused
// 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:
    __fastcall TCleaningOrder();
    __fastcall ~TCleaningOrder();
    void __fastcall ProcessOrder();
    void __fastcall DisplayReceipt();
private:
    int NumberOfShirts;
    int NumberOfPants;
    int NumberOfMisc;
    double TotalPriceShirts;
    double TotalPricePants;
    double TotalPriceMisc;
    double TotalOrder;
};
//---------------------------------------------------------------------------
__fastcall TCleaningOrder::TCleaningOrder()
    :  NumberOfShirts(0), NumberOfPants(0), NumberMisc(0)
{
}
//---------------------------------------------------------------------------
__fastcall TCleaningOrder::~TCleaningOrder()
{
}
//---------------------------------------------------------------------------
void __fastcall 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 __fastcall 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();

    cout << "\n\nPress any key to continue...";
    getchar();
    getchar();
    return 0;
}
//---------------------------------------------------------------------------

Practical Learning: Class' Methods and their Exceptions

  1. As an example, change the program as follows:
     
    //---------------------------------------------------------------------------
    #include <iostream.h>
    #pragma hdrstop
    //---------------------------------------------------------------------------
    #pragma argsused
    class TCalculator
    {
    public:
        __fastcall TCalculator();
        void __fastcall RequestOperands();
        void __fastcall setOperation(const double x, const char p, const double y);
        double __fastcall CalcResult() const;
        void __fastcall DisplayResult() const;
    private:
        double Operand1;
        double Operand2;
        char Operator;
    };
    //---------------------------------------------------------------------------
    __fastcall TCalculator::TCalculator()
    {
    }
    //---------------------------------------------------------------------------
    void __fastcall TCalculator::RequestOperands()
    {
        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;
    
            setOperation(Operand1, Operator, Operand2);
        }
        catch(const char c)
        {
            cout << "Bad Operator: " << c << " is not a valid operator";
        }
    }
    //---------------------------------------------------------------------------
    void __fastcall TCalculator::setOperation(const double Num,
                                  const char c, const double Denom)
    {
        Operand1 = Num;
        Operator = c;
        Operand2 = Denom;
    }
    //---------------------------------------------------------------------------
    double __fastcall 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 __fastcall TCalculator::DisplayResult() const
    {
        double Result;
        CalcResult();
    
        Result = CalcResult();
        // Display the result of the operation
    	cout << "\n" << Operand1 << " " << Operator << " "
    	     << Operand2 << " = " << Result;
    }
    //---------------------------------------------------------------------------
    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";
        cout << "To proceed, enter\n";
    
        Calc.RequestOperands();
        Calc.DisplayResult();
    
        cout << "\nPress any key to continue...";
        getchar();
        return 0;
    }
    //---------------------------------------------------------------------------
  2. Test the program and return to Bcb

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 __fastcall 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.h>
#include <iomanip.h>
#include <conio.h>
#pragma hdrstop
//---------------------------------------------------------------------------
#pragma argsused
// 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 __fastcall setShirts(const int Shirts);
    int __fastcall getShirts() { return NumberOfShirts; }
    void __fastcall setPants(const int Pants);
    int __fastcall getPants() { return NumberOfPants; }
    void __fastcall setMisc(const int Misc);
    int __fastcall getMisc() { return NumberOfMisc; }
    __fastcall TCleaningOrder();
    __fastcall ~TCleaningOrder();
    void __fastcall ProcessOrder();
    void __fastcall DisplayReceipt();
private:
    int NumberOfShirts;
    int NumberOfPants;
    int NumberOfMisc;
    double TotalPriceShirts;
    double TotalPricePants;
    double TotalPriceMisc;
    double TotalOrder;
};
//---------------------------------------------------------------------------
__fastcall TCleaningOrder::TCleaningOrder()
    :  NumberOfShirts(0), NumberOfPants(0), NumberOfMisc(0)
{
}
//---------------------------------------------------------------------------
__fastcall TCleaningOrder::~TCleaningOrder()
{
}
//---------------------------------------------------------------------------
void __fastcall TCleaningOrder::setShirts(const int Shirts)
{
    if( isdigit(Shirts) )
        throw Shirts;
    NumberOfShirts = Shirts;
}
//---------------------------------------------------------------------------
void __fastcall TCleaningOrder::setPants(const int Pants)
{
    if( isdigit(Pants) )
        throw Pants;
    NumberOfPants = Pants;
}
//---------------------------------------------------------------------------
void __fastcall TCleaningOrder::setMisc(const int Misc)
{
    if( isdigit(Misc) )
        throw Misc;
    NumberOfMisc = Misc;
}
//---------------------------------------------------------------------------
void __fastcall 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 __fastcall 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();

    cout << "\n\nPress any key to continue...";
    getchar();
    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).

  1. Change the content of the file as follows:
     
    //---------------------------------------------------------------------------
    #include <iostream.h>
    #pragma hdrstop
    //---------------------------------------------------------------------------
    #pragma argsused
    class TCalculator
    {
    public:
        __fastcall TCalculator();
    	__fastcall TCalculator(char* Oper1, char Opr, char* Oper2);
        void __fastcall RequestOperands();
        void __fastcall setOperand1(const char* Oper1);
        double __fastcall getOperand1() const;
        void __fastcall setOperand2(const char *Oper2);
        double __fastcall getOperand2() const;
        void __fastcall setOperator(const char Opr);
        char __fastcall getOperator() const;
        void __fastcall setOperation(const char* x, const char p, const char* y);
        double __fastcall CalcResult() const;
        void __fastcall DisplayResult() const;
    private:
        double Operand1;
        double Operand2;
        char Operator;
    };
    //---------------------------------------------------------------------------
    __fastcall TCalculator::TCalculator()
    {
    }
    //---------------------------------------------------------------------------
    __fastcall TCalculator::TCalculator(char* Oper1, char Opr, char* Oper2)
    {
    	setOperand1(Oper1);
    	setOperator(Opr);
    	setOperand2(Oper2);
    }
    //---------------------------------------------------------------------------
    void __fastcall 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 __fastcall TCalculator::setOperand1(const char* Oper1)
    {
    	for(unsigned int i = 0; i < strlen(Oper1); i++)
    		if( (!isdigit(Oper1[i])) && (Oper1[i] != '.') )
    			throw Oper1;
    	
    	Operand1 = atof(Oper1);
    }
    
    //---------------------------------------------------------------------------
    double __fastcall TCalculator::getOperand1() const
    {
    	return Operand1;
    }
    
    //---------------------------------------------------------------------------
    void __fastcall TCalculator::setOperand2(const char* Oper2)
    {
    	for(unsigned int i = 0; i < strlen(Oper2); i++)
    		if( (!isdigit(Oper2[i])) && (Oper2[i] != '.') )
    			throw Oper2;
    	
    	Operand2 = atof(Oper2);
    }
    
    //---------------------------------------------------------------------------
    double __fastcall TCalculator::getOperand2() const
    {
    	return Operand2;
    }
    
    //---------------------------------------------------------------------------
    void __fastcall TCalculator::setOperator(const char Symbol)
    {
    	if(Symbol != '+' && Symbol != '-' &&
    	   Symbol != '*' && Symbol != '/')
    		throw Symbol;
    
    	Operator = Symbol;
    }
    //---------------------------------------------------------------------------
    char __fastcall TCalculator::getOperator() const
    {
    	return Operator;
    }
    //---------------------------------------------------------------------------
    void __fastcall TCalculator::setOperation(const char* Oper1,
                            const char Opr, const char* Oper2)
    {
    	setOperand1(Oper1);
    	setOperator(Opr);
    	setOperand2(Oper2);
    }
    //---------------------------------------------------------------------------
    double __fastcall 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 __fastcall TCalculator::DisplayResult() const
    {
        double Result;
        CalcResult();
    
        Result = CalcResult();
        // Display the result of the operation
    	cout << "\n" << Operand1 << " " << Operator << " "
    	     << Operand2 << " = " << Result;
    }
    //---------------------------------------------------------------------------
    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();
    
        cout << "\nPress any key to continue...";
        getchar();
        return 0;
    }
    //---------------------------------------------------------------------------
  2. Test the program and return to Bcb.
  3. Just as done with the functions, you can show that a function throws one or more exceptions in its declaration. To do this, in the class, on the right side of the function that throws an exception, type the throw keyword followed by parentheses in which you would provide the type of exception that the following would throw.
    To apply this, change the function as follows:
     
    //---------------------------------------------------------------------------
    #include <iostream.h>
    #pragma hdrstop
    //---------------------------------------------------------------------------
    #pragma argsused
    class TCalculator
    {
    public:
        __fastcall TCalculator();
    	__fastcall TCalculator(char* Oper1, char Opr, char* Oper2);
        void __fastcall RequestOperands();
        void __fastcall setOperand1(const char* Oper1) throw(const char*);
        double __fastcall getOperand1() const;
        void __fastcall setOperand2(const char *Oper2) throw(const char*);
        double __fastcall getOperand2() const;
        void __fastcall setOperator(const char Opr) throw(const char);
        char __fastcall getOperator() const;
        void __fastcall setOperation(const char* x, const char p, const char* y);
        double __fastcall CalcResult() const;
        void __fastcall DisplayResult() const;
    private:
        double Operand1;
        double Operand2;
        char Operator;
    };
    //---------------------------------------------------------------------------
    __fastcall TCalculator::TCalculator()
    {
    }
    //---------------------------------------------------------------------------
    __fastcall TCalculator::TCalculator(char* Oper1, char Opr, char* Oper2)
    {
    	setOperand1(Oper1);
    	setOperator(Opr);
    	setOperand2(Oper2);
    }
    //---------------------------------------------------------------------------
    void __fastcall 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 __fastcall 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 __fastcall TCalculator::getOperand1() const
    {
    	return Operand1;
    }
    //---------------------------------------------------------------------------
    void __fastcall 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 __fastcall TCalculator::getOperand2() const
    {
    	return Operand2;
    }
    //---------------------------------------------------------------------------
    void __fastcall TCalculator::setOperator(const char Symbol) throw(const char)
    {
    	if(Symbol != '+' && Symbol != '-' &&
    	   Symbol != '*' && Symbol != '/')
    		throw Symbol;
    
    	Operator = Symbol;
    }
    //---------------------------------------------------------------------------
    .
    .
    .
  4. Test the program and return to Bcb.

 

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

  1. Create a new C++ Console Application using the Console Wizard.
  2. Create a folder called Exceptional2
  3. Save Unit1 as Main and save the project as Exceptions
  4. To create a new class, on the main menu, click File -> New... or File -> New -> Other...
  5. From the New property sheet of the New Object dialog box, double-click Unit
  6. Save the unit as Calculator
  7. Click the Calculator.h tab and change the content of the file as follows:
     
    //---------------------------------------------------------------------------
    
    #ifndef CalculatorH
    #define CalculatorH
    //---------------------------------------------------------------------------
    class TCalculator
    {
    public:
        __fastcall TCalculator();
        __fastcall TCalculator(char* Oper1, char Opr, char* Oper2);
        void __fastcall RequestOperands();
        void __fastcall setOperand1(const char* Oper1) throw(const char*);
        double __fastcall getOperand1() const;
        void __fastcall setOperand2(const char *Oper2) throw(const char*);
        double __fastcall getOperand2() const;
        void __fastcall setOperator(const char Opr) throw(const char);
        char __fastcall getOperator() const;
        void __fastcall setOperation(const char* x, const char p, const char* y);
        double __fastcall CalcResult() const;
        void __fastcall DisplayResult() const;
    private:
        double Operand1;
        double Operand2;
        char Operator;
    };
    //---------------------------------------------------------------------------
    #endif
  8. Click the Calculator.cpp tab and change its content as follows:
     
    //---------------------------------------------------------------------------
    #include <iostream.h>
    #pragma hdrstop
    
    #include "Calculator.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    //---------------------------------------------------------------------------
    __fastcall TCalculator::TCalculator()
    {
    }
    //---------------------------------------------------------------------------
    __fastcall TCalculator::TCalculator(char* Oper1, char Opr, char* Oper2)
    {
    	setOperand1(Oper1);
    	setOperator(Opr);
    	setOperand2(Oper2);
    }
    //---------------------------------------------------------------------------
    void __fastcall 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 __fastcall 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 __fastcall TCalculator::getOperand1() const
    {
    	return Operand1;
    }
    //---------------------------------------------------------------------------
    void __fastcall 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 __fastcall TCalculator::getOperand2() const
    {
    	return Operand2;
    }
    //---------------------------------------------------------------------------
    void __fastcall TCalculator::setOperator(const char Symbol) throw(const char)
    {
    	if(Symbol != '+' && Symbol != '-' &&
    	   Symbol != '*' && Symbol != '/')
    		throw Symbol;
    
    	Operator = Symbol;
    }
    //---------------------------------------------------------------------------
    char __fastcall TCalculator::getOperator() const
    {
    	return Operator;
    }
    //---------------------------------------------------------------------------
    void __fastcall TCalculator::setOperation(const char* Oper1,
                            const char Opr, const char* Oper2)
    {
    	setOperand1(Oper1);
    	setOperator(Opr);
    	setOperand2(Oper2);
    }
    //---------------------------------------------------------------------------
    double __fastcall 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 __fastcall TCalculator::DisplayResult() const
    {
        double Result;
        CalcResult();
    
        Result = CalcResult();
        // Display the result of the operation
    	cout << "\n" << Operand1 << " " << Operator << " "
    	     << Operand2 << " = " << Result;
    }
    //---------------------------------------------------------------------------
  9. Click the Main.cpp tab and change its content as follows:
     
    //---------------------------------------------------------------------------
    #include <iostream.h>
    #pragma hdrstop
    #include "Calculator.h"
    //---------------------------------------------------------------------------
    #pragma argsused
    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();
    
        cout << "\n\nPress any key to continue...";
        getchar();
        getchar();
        return 0;
    }
    //---------------------------------------------------------------------------
  10. Test the program.

Previous Copyright © 2002 FunctionX Home