Chapter 16 Exceptions and Templates

16.1 Exceptions

Concept:
  • Exceptions are used to signal errors or other unexpected events that happen while a program is running.
  • Simple error testing can be done with if statements, but this approach is unreliable for functions that need to return a value, as the error-indicating return value (like 0) might also be a valid result.

Throwing an Exception

  • Exceptions provide a more robust way to handle complex errors. An exception is an object or value that signals an error.
  • When an error occurs, an exception is “thrown” using the throw keyword.
double divide(int numerator, int denominator)
{
    if (denominator == 0)
        throw "ERROR: Cannot divide by zero.\n";
    else
        return static_cast<double>(numerator) / denominator;
}

Throwing an Exception

  • The line with the throw statement is called the throw point.
  • When an exception is thrown, the function aborts and control is passed to an exception handler.

Handling an Exception

  • To handle an exception, you use a try/catch construct.
  • The try block contains code that might throw an exception.
  • The catch block is the exception handler; it immediately follows the try block and contains code to handle the exception.
try
{
    quotient = divide(num1, num2);
    cout << "The quotient is " << quotient << endl;
}
catch (string exceptionString)
{
    cout << exceptionString;
}

Handling an Exception

  • The catch block has an exception parameter that specifies the data type of the exception it can handle.
  • The following program demonstrates the complete throw, try, and catch mechanism.

🗊 Program 16-1

 #include <iostream>
 #include <string>
 using namespace std;

 double divide(int, int);

 int main()
 {
     int num1, num2;  
     double quotient;

     cout << "Enter two numbers: ";
     cin >> num1 >> num2;

     try
     {
         quotient = divide(num1, num2);
         cout << "The quotient is " << quotient << endl;
     }
     catch (string exceptionString)
     {
         cout << exceptionString;
     }

     cout << "End of the program.\n";
     return 0;
 }


 double divide(int numerator, int denominator)
 {
     if (denominator == 0)
     {
         string exceptionString = "ERROR: Cannot divide by zero.\n";
         throw exceptionString;
     }

     return static_cast<double>(numerator) / denominator;
 }

💻 Program Output




💻 Program Output




  • When an exception is thrown, the program jumps from the throw point to the catch block. After the catch block finishes, execution resumes at the statement immediately following the try/catch construct.

  • If no exception is thrown in the try block, the catch block is skipped entirely.

What if an Exception Is Not Caught?

  • If an exception is thrown outside of a try block, or if there is no catch block with a matching data type, the exception will go uncaught.
  • An uncaught exception will cause the program to abort.

Object-Oriented Exception Handling with Classes

  • A common object-oriented approach is to define special classes to represent exceptions.
  • The Rectangle class below is modified to throw an exception if a negative value is provided for width or length.
Contents of Rectangle.h (Version 1)
 #ifndef RECTANGLE_H
 #define RECTANGLE_H

 class Rectangle
 {
     private:
         double width;    
         double length;   
     public:
         class NegativeSize
             { };        

         Rectangle()
             { width = 0.0; length = 0.0; }

         void setWidth(double);
         void setLength(double);

         double getWidth() const
             { return width; }

         double getLength() const
             { return length; }

         double getArea() const
             { return width * length; }
 };
 #endif
  • An empty class, NegativeSize, is declared. Its name is used to identify the type of error.
Contents of Rectangle.cpp (Version 1)
 #include "Rectangle.h"


 void Rectangle::setWidth(double w)
 {
     if (w >= 0)
         width = w;
     else
         throw NegativeSize();
 }


 void Rectangle::setLength(double len)
 {
     if (len >= 0)
         length = len;
     else
         throw NegativeSize();
 }
  • The setWidth and setLength functions throw an instance of the NegativeSize class if their argument is negative.
  • Code that uses the Rectangle class can then use a catch block to handle NegativeSize exceptions.

🗊 Program 16-2

 #include <iostream>
 #include "Rectangle.h"
 using namespace std;

 int main()
 {
     double width;
     double length;

     Rectangle myRectangle;

     cout << "Enter the rectangle's width: ";
     cin >> width;
     cout << "Enter the rectangle's length: ";
     cin >> length;

     try
     {
         myRectangle.setWidth(width);
         myRectangle.setLength(length);
         cout << "The area of the rectangle is "
              << myRectangle.getArea() << endl;
     }
     catch (Rectangle::NegativeSize)
     {
         cout << "Error: A negative value was entered.\n";
     }
     cout << "End of the program.\n";

     return 0;
 }

💻 Program Output





💻 Program Output





  • The catch statement specifies the type of exception it handles (Rectangle::NegativeSize). Since the exception class is nested, its name must be qualified with the scope resolution operator.

Multiple Exceptions

  • A program can be designed to handle multiple types of errors.
  • This requires defining a different exception type for each error and using a separate catch block for each exception type.
  • The Rectangle class can be modified to throw a NegativeWidth exception for an invalid width and a NegativeLength exception for an invalid length.
Contents of Rectangle.h (Version 2)
 #ifndef RECTANGLE_H
 #define RECTANGLE_H

 class Rectangle
 {
     private:
         double width;     
         double length;    
     public:
         class NegativeWidth
             { };

         class NegativeLength
             { };

         Rectangle()
             { width = 0.0; length = 0.0; }

         void setWidth(double);
         void setLength(double);

         double getWidth() const
             { return width; }

         double getLength() const
             { return length; }

         double getArea() const
             { return width * length; }
 };
 #endif
Contents of Rectangle.cpp (Version 2)
 #include "Rectangle.h"


 void Rectangle::setWidth(double w)
 {
     if (w >= 0)
         width = w;
     else
         throw NegativeWidth();
 }


 void Rectangle::setLength(double len)
 {
     if (len >= 0)
         length = len;
     else
         throw NegativeLength();
 }
  • When an exception is thrown, C++ searches for a catch block that matches the exception’s type.

🗊 Program 16-3

 #include <iostream>
 #include "Rectangle.h"
 using namespace std;

 int main()
 {
     double width;
     double length;

     Rectangle myRectangle;

     cout << "Enter the rectangle's width: ";
     cin >> width;
     cout << "Enter the rectangle's length: ";
     cin >> length;

     try
     {
         myRectangle.setWidth(width);
         myRectangle.setLength(length);
         cout << "The area of the rectangle is "
              << myRectangle.getArea() << endl;
     }
     catch (Rectangle::NegativeWidth)
     {
         cout << "Error: A negative value was given "
              << "for the rectangle's width.\n";
     }
     catch (Rectangle::NegativeLength)
     {
         cout << "Error: A negative value was given "
              << "for the rectangle's length.\n";
     }

     cout << "End of the program.\n";
     return 0;
 }

💻 Program Output





💻 Program Output





💻 Program Output





Using Exception Handlers to Recover from Errors

  • Exception handlers can be used to recover from errors, for example, by prompting the user to re-enter valid data inside a loop.

🗊 Program 16-4

 #include <iostream>
 #include "Rectangle.h"
 using namespace std;

 int main()
 {
     double width;             
     double length;            
     bool tryAgain = true;     

     Rectangle myRectangle;

     cout << "Enter the rectangle's width: ";
     cin >> width;

     while (tryAgain)
     {
         try
         {
             myRectangle.setWidth(width);
             tryAgain = false;
         }
         catch (Rectangle::NegativeWidth)
         {
             cout << "Please enter a nonnegative value: ";
             cin >> width;
         }
     }

     cout << "Enter the rectangle's length: ";
     cin >> length;

     tryAgain = true;
     while (tryAgain)
     {
         try
         {
             myRectangle.setLength(length);
             tryAgain = false;
         }
         catch (Rectangle::NegativeLength)
         {
             cout << "Please enter a nonnegative value: ";
             cin >> length;
         }
     }

     cout << "The rectangle's area is "
          << myRectangle.getArea() << endl;
     return 0;
 }

💻 Program Output






Extracting Data from the Exception Class

  • An exception class can be designed with member variables and functions to pass data back to the exception handler.
  • To do this, the exception class needs a constructor to store the data and an accessor function to retrieve it.
  • When throwing the exception, an argument is passed to the exception class’s constructor.
throw NegativeWidth(w);
  • The catch block then defines a parameter object to receive the exception object and call its member functions.
catch (Rectangle::NegativeWidth e)
{
    cout << "Error: " << e.getValue() << " is invalid.\n";
}
Contents of Rectangle.h (Version 3)
 #ifndef RECTANGLE_H
 #define RECTANGLE_H

 class Rectangle
 {
     private:
         double width;     
         double length;    
     public:
         class NegativeWidth
         {
         private:
             double value;
         public:
             NegativeWidth(double val)
                 { value = val; }

             double getValue() const
                 { return value; }
         };

         class NegativeLength
         {
         private:
             double value;
         public:
             NegativeLength(double val)
                 { value = val; }

             double getValue() const
                 { return value; }
         };

         Rectangle()
             { width = 0.0; length = 0.0; }

         void setWidth(double);
         void setLength(double);

         double getWidth() const
             { return width; }

         double getLength() const
             { return length; }

         double getArea() const
             { return width * length; }
 };
 #endif
Contents of Rectangle.cpp (Version 3)
 #include "Rectangle.h"


 void Rectangle::setWidth(double w)
 {
     if (w >= 0)
         width = w;
     else
         throw NegativeWidth(w);
 }


 void Rectangle::setLength(double len)
 {
     if (len >= 0)
         length = len;
     else
         throw NegativeLength(len);
 }

🗊 Program 16-5

 #include <iostream>
 #include "Rectangle.h"
 using namespace std;

 int main()
 {
     double width;
     double length;

     Rectangle myRectangle;

     cout << "Enter the rectangle's width: ";
     cin >> width;
     cout << "Enter the rectangle's length: ";
     cin >> length;

     try
     {
         myRectangle.setWidth(width);
         myRectangle.setLength(length);
         cout << "The area of the rectangle is "
              << myRectangle.getArea() << endl;
     }
     catch (Rectangle::NegativeWidth e)
     {
         cout << "Error: " << e.getValue()
              << " is an invalid value for the"
              << " rectangle's width.\n";
     }
     catch (Rectangle::NegativeLength e)
     {
         cout << "Error: " << e.getValue()
              << " is an invalid value for the"
              << " rectangle's length.\n";
     }

     cout << "End of the program.\n";
     return 0;
 }

💻 Program Output





💻 Program Output





Unwinding the Stack

  • When an exception is thrown, the function containing the throw statement immediately terminates.
  • If the function was called by another function, the calling function also terminates, and this process continues up the chain of function calls until a try block is found. This process is known as unwinding the stack.
  • During stack unwinding, the destructors of any local objects created in the terminating functions are called.

Rethrowing an Exception

  • It’s possible for an inner catch block to handle an exception and then rethrow it to an outer catch block.
  • This is done using a throw statement with no arguments inside the inner catch block.
catch(exception1)
{
    throw; 
}

Handling the bad_alloc Exception

  • The new operator throws a bad_alloc exception when it fails to allocate memory.
  • To handle this, include the <new> header file and place the new operator call inside a try block.
  • A catch block can then be written to handle the bad_alloc exception.

🗊 Program 16-6

 #include <iostream>
 #include <new>        
 using namespace std;

 int main()
 {
     double *ptr = nullptr; 

     try
     {
         ptr = new double [10000];
     }
     catch (bad_alloc)
     {
         cout << "Insufficient memory.\n";
     }

     return 0;
 }

Checkpoint

  1. 16.1 What is the difference between a try block and a catch block?

  2. 16.2 What happens if an exception is thrown, but not caught?

  3. 16.3 If multiple exceptions can be thrown, how does the catch block know which exception to catch?

  4. 16.4 After the catch block has handled the exception, where does program execution resume?

  5. 16.5 How can an exception pass data back to the exception handler?


16.2 Function Templates

Concept:
  • A function template is a generic function “mold” that can work with any data type.
  • The programmer defines the function’s logic using generic type parameters instead of actual data types.
  • The compiler generates a specific version of the function (a template function) based on the data types used in a function call.

Introduction

  • Function templates are more convenient than overloaded functions when the same logic applies to different data types.
  • Instead of writing multiple versions of a function, you write a single template.
  • A function template uses a type parameter to represent a generic data type.
template <class T>
T square(T number)
{
    return number * number;
}

Writing a Function Template

  • The template begins with a template prefix: template <class T>.
  • T is the type parameter, which stands for a data type that will be specified when the function is called.
  • The compiler automatically generates the appropriate function code based on the argument types. For example, square(4) generates an int version, while square(6.2) generates a double version.

🗊 Program 16-7

 #include <iostream>
 #include <iomanip>
 using namespace std;

 template <class T>
 T square(T number)
 {
     return number * number;
 }

 int main()
 {
     int userInt;       
     double userDouble; 

     cout << setprecision(5);
     cout << "Enter an integer and a floating-point value: ";
     cin  >> userInt >> userDouble;
     cout << "Here are their squares: ";
     cout << square(userInt) << " and "
          << square(userDouble) << endl;
    return 0;
 }

💻 Program Output



Note:
  • Every type parameter defined in a template’s prefix must be used in the function’s parameter list.
  • A template itself doesn’t use memory; a function instance is only created when the compiler encounters a call to it.

  • The template definition must appear before any calls to the function, so it’s often placed at the top of a file or in a header file.

🗊 Program 16-8

 #include <iostream>
 using namespace std;

 template <class T>
 void swapVars(T &var1, T &var2)
 {
     T temp;

     temp = var1;
     var1 = var2;
     var2 = temp;
 }

 int main()
 {
     char firstChar, secondChar;       
     int firstInt, secondInt;          
     double firstDouble, secondDouble; 

     cout << "Enter two characters: ";
     cin >> firstChar >> secondChar;
     swapVars(firstChar, secondChar);
     cout << firstChar << " " << secondChar << endl;

     cout << "Enter two integers: ";
     cin >> firstInt >> secondInt;
     swapVars(firstInt, secondInt);
     cout << firstInt << " " << secondInt << endl;

     cout << "Enter two floating-point numbers: ";
     cin >> firstDouble >> secondDouble;
     swapVars(firstDouble, secondDouble);
     cout << firstDouble << " " << secondDouble << endl;
     return 0;
 }

💻 Program Output







Using Operators in Function Templates

  • If you pass a class object to a function template, the class must support all operations (like *, >, ==) that the template performs on it. This may require operator overloading.

Function Templates with Multiple Types

  • A function template can have more than one generic type parameter. Each must be listed in the template prefix.
  • This allows the template to accept arguments of different types.

🗊 Program 16-9

 #include <iostream>
 using namespace std;

 template <class T1, class T2>
 int largest(const T1 &var1, T2 &var2)
 {
     if (sizeof(var1) > sizeof(var2))
         return sizeof(var1);
     else
         return sizeof(var2);
 }

 int main()
 {
     int i = 0;
     char c = ' ';
     float f = 0.0;
     double d = 0.0;

     cout << "Comparing an int and a double, the largest\n"
          << "of the two is " << largest(i, d) << " bytes.\n";

     cout << "Comparing a char and a float, the largest\n"
          << "of the two is " << largest(c, f) << " bytes.\n";

     return 0;
 }

💻 Program Output





Note:
  • Each type parameter in the template prefix must be used in the template’s definition.

Overloading with Function Templates

  • Function templates can be overloaded as long as they have different parameter lists.
  • A program can also have both a regular function and a template version of a function with the same name, as long as their parameter lists differ.

🗊 Program 16-10

 #include <iostream>
 using namespace std;

 template <class T>
 T sum(T val1, T val2)
 {
     return val1 + val2;
 }

 template <class T>
 T sum(T val1, T val2, T val3)
 {
     return val1 + val2 + val3;
 }

 int main()
 {
     double num1, num2, num3;

     cout << "Enter two values: ";
     cin >> num1 >> num2;
     cout << "Their sum is " << sum(num1, num2) << endl;

     cout << "Enter three values: ";
     cin >> num1 >> num2 >> num3;
     cout << "Their sum is " << sum(num1, num2, num3) << endl;
     return 0;
 }

💻 Program Output






16.3 Focus on Software Engineering: Where to Start When Defining Templates

  • A good strategy for writing a function template is to first write it as a regular function with a specific data type.
  • After the regular function is tested and debugged, you can convert it into a template by adding the template prefix and replacing the specific data types with the generic type parameter.

Checkpoint

  1. 16.6 When does the compiler actually generate code for a function template?

  2. 16.7 The following function accepts an int argument and returns half of its value as a double:

    double half(int number)
    {
        return number / 2.0;
    }

    Write a template that will implement this function to accept an argument of any type.

  3. 16.8 What must you be sure of when passing a class object to a function template that uses an operator, such as * or >?

  4. 16.9 What is the best method for writing a function template?


16.4 Class Templates

Concept:
  • Class templates are used to create generic classes that can work with multiple data types. This avoids duplicating code for classes that have the same logic but handle different types (e.g., an array class for ints, doubles, etc.).
  • A class template is declared by placing a template prefix before the class declaration.
  • Inside the class, the generic type parameter (e.g., T) is used in place of a specific data type.
Contents of SimpleVector.h
 #ifndef SIMPLEVECTOR_H
 #define SIMPLEVECTOR_H
 #include <iostream>
 #include <new>      
 #include <cstdlib>  
 using namespace std;

 template <class T>
 class SimpleVector
 {
 private:
     T *aptr;         
     int arraySize;   
     void memError(); 
     void subError(); 

 public:
     SimpleVector()
         { aptr = 0; arraySize = 0;}

     SimpleVector(int);

     SimpleVector(const SimpleVector &);

     ~SimpleVector();

     int size() const
         { return arraySize; }

     T getElementAt(int position);

     T &operator[](const int &);
 };


 template <class T>
 SimpleVector<T>::SimpleVector(int s)
 {
     arraySize = s;
     try
     {
         aptr = new T [s];
     }
     catch (bad_alloc)
     {
         memError();
     }

     for (int count = 0; count < arraySize; count++)
         *(aptr + count) = 0;
 }


 template <class T>
 SimpleVector<T>::SimpleVector(const SimpleVector &obj)
 {
     arraySize = obj.arraySize;

     aptr = new T [arraySize];
     if (aptr == 0)
         memError();

     for(int count = 0; count < arraySize; count++)
         *(aptr + count) = *(obj.aptr + count);
 }


 template <class T>
 SimpleVector<T>::~SimpleVector()
 {
     if (arraySize > 0)
         delete [] aptr;
 }


 template <class T>
 void SimpleVector<T>::memError()
 {
     cout << "ERROR:Cannot allocate memory.\n";
     exit(EXIT_FAILURE);
 }


 template <class T>
 void SimpleVector<T>::subError()
 {
     cout << "ERROR: Subscript out of range.\n";
     exit(EXIT_FAILURE);
 }


 template <class T>
 T SimpleVector<T>::getElementAt(int sub)
 {
     if (sub < 0 || sub >= arraySize)
         subError();
     return aptr[sub];
 }


 template <class T>
 T &SimpleVector<T>::operator[](const int &sub)
 {
     if (sub < 0 || sub >= arraySize)
         subError();
     return aptr[sub];
 }
 #endif
Note:
  • Members that do not depend on the type parameter, like arraySize, can still use specific data types like int.

Defining Objects of the Class Template

  • To define an object of a class template, you must specify the data type to be used for the type parameter.
  • This is done by writing the data type in angle brackets after the class name.
SimpleVector<int> intTable(10);
SimpleVector<double> doubleTable(10);

🗊 Program 16-11

 #include <iostream>
 #include "SimpleVector.h"
 using namespace std;

 int main()
 {
     const int SIZE = 10; 
     int count;           

     SimpleVector<int> intTable(SIZE);

     SimpleVector<double> doubleTable(SIZE);

     for (count = 0; count < SIZE; count++)
     {
         intTable[count] = (count * 2);
         doubleTable[count] = (count * 2.14);
     }

     cout << "These values are in intTable:\n";
     for (count = 0; count < SIZE; count++)
         cout << intTable[count] << " ";
     cout << endl;
     cout << "These values are in doubleTable:\n";
     for (count = 0; count < SIZE; count++)
         cout << doubleTable[count] << " ";
     cout << endl;

     cout << "\nAdding 5 to each element of intTable"
          << " and doubleTable.\n";
     for (count = 0; count < SIZE; count++)
     {
         intTable[count] = intTable[count] + 5;
         doubleTable[count] = doubleTable[count] + 5.0;
     }

     cout << "These values are in intTable:\n";
     for (count = 0; count < SIZE; count++)
         cout << intTable[count] << " ";
     cout << endl;
     cout << "These values are in doubleTable:\n";
     for (count = 0; count < SIZE; count++)
         cout << doubleTable[count] << " ";
     cout << endl;

     cout << "\nIncrementing each element of intTable and"
          << " doubleTable.\n";
     for (count = 0; count < SIZE; count++)
     {
         intTable[count]++;
         doubleTable[count]++;
     }

     cout << "These values are in intTable:\n";
     for (count = 0; count < SIZE; count++)
         cout << intTable[count] << " ";
     cout << endl;
     cout << "These values are in doubleTable:\n";
     for (count = 0; count < SIZE; count++)
         cout << doubleTable[count] << " ";
     cout << endl;

     return 0;
 }

💻 Program Output















Class Templates and Inheritance

  • A class template can be derived from another class template.
  • When specifying the base class, its type parameter must also be included, for example: class SearchableVector : public SimpleVector<T>.
Contents of SearchableVector.h
 #ifndef SEARCHABLEVECTOR_H
 #define SEARCHABLEVECTOR_H
 #include "SimpleVector.h"

 template <class T>
 class SearchableVector : public SimpleVector<T>
 {
 public:
     SearchableVector() : SimpleVector<T>()
         { }

     SearchableVector(int size) : SimpleVector<T>(size)
         { }

     SearchableVector(const SearchableVector &);

     int findItem(const T);
 };


 template <class T>
 SearchableVector<T>::SearchableVector(const SearchableVector &obj) :
                   SimpleVector<T>(obj.size())
 {
     for(int count = 0; count < this->size(); count++)
         this->operator[](count) = obj[count];
 }


 template <class T>
 int SearchableVector<T>::findItem(const T item)
 {
     for (int count = 0; count <= this->size(); count++)
     {
         if (getElementAt(count) == item)
             return count;
     }
     return -1;
 }
 #endif

🗊 Program 16-12

 #include <iostream>
 #include "SearchableVector.h"
 using namespace std;

 int main()
 {
     const int SIZE = 10; 
     int count;           
     int result;          

     SearchableVector<int> intTable(SIZE);
     SearchableVector<double> doubleTable(SIZE);

     for (count = 0; count < SIZE; count++)
     {
         intTable[count] = (count * 2);
         doubleTable[count] = (count * 2.14);
     }

     cout << "These values are in intTable:\n";
     for (count = 0; count < SIZE; count++)
         cout << intTable[count] << " ";
     cout << endl << endl;
     cout << "These values are in doubleTable:\n";
     for (count = 0; count < SIZE; count++)
         cout << doubleTable[count] << " ";
     cout << endl;

     cout << "\nSearching for 6 in intTable.\n";
     result = intTable.findItem(6);
     if (result == -1)
         cout << "6 was not found in intTable.\n";
     else
         cout << "6 was found at subscript " << result << endl;

     cout << "\nSearching for 12.84 in doubleTable.\n";
     result = doubleTable.findItem(12.84);
     if (result == -1)
         cout << "12.84 was not found in doubleTable.\n";
     else
         cout << "12.84 was found at subscript " << result << endl;
     return 0;
 }

💻 Program Output









Specialized Templates

  • A specialized template provides a specific implementation for a template that is designed to work with a particular data type.
  • This is useful when a generic template does not work correctly for a specific type, such as C-strings.
  • The declaration for a specialized template replaces the type parameter with the actual data type.
class SimpleVector<char *>

Checkpoint

  1. 16.10 Suppose your program uses a class template named List, which is defined as

    template<class T>
    class List
    {
    };

    Give an example of how you would use int as the data type in the definition of a List object. (Assume the class has a default constructor.)

  2. 16.11 As the following Rectangle class is written, the width and length members are doubles. Rewrite the class as a template that will accept any data type for these members.

    class Rectangle
    {
        private:
            double width;
            double length;
        public:
            void setData(double w, double l)
                { width = w; length = l;}
            double getWidth()
                { return width; }
            double getLength()
                { return length; }
            double getArea()
                { return width * length; }
    };

Review Questions and Exercises

Short Answer

  1. What is a throw point?

  2. What is an exception handler?

  3. Explain the difference between a try block and a catch block.

  4. What happens if an exception is thrown, but not caught?

  5. What is “unwinding the stack”?

  6. What happens if an exception is thrown by a class’s member function?

  7. How do you prevent a program from halting when the new operator fails to allocate memory?

  8. Why is it more convenient to write a function template than a series of overloaded functions?

  9. Why must you be careful when writing a function template that uses operators such as [] with its parameters?

Fill-in-the-Blank

  1. The line containing a throw statement is known as the ___________ .

  2. The ___________ block contains code that directly or indirectly might cause an exception to be thrown.

  3. The ___________ block handles an exception.

  4. When writing function or class templates, you use a(n) ___________ to specify a generic data type.

  5. The beginning of a template is marked by a(n) ___________ .

  6. When defining objects of class templates, the ___________ you wish to pass into the type parameter must be specified.

  7. A(n) ___________ template works with a specific data type.

Algorithm Workbench

  1. Write a function that searches a numeric array for a specified value. The function should return the subscript of the element containing the value if it is found in the array. If the value is not found, the function should throw an exception.

  2. Write a function that dynamically allocates a block of memory and returns a char pointer to the block. The function should take an integer argument that is the amount of memory to be allocated. If the new operator cannot allocate the memory, the function should return a null pointer.

  3. Make the function you wrote in Question 17 a template.

  4. Write a template for a function that displays the contents of an array of any type.

True or False

  1. T F There can be only one catch block in a program.

  2. T F When an exception is thrown, but not caught, the program ignores the error.

  3. T F Data may be passed with an exception by storing it in members of an exception class.

  4. T F Once an exception has been thrown, it is not possible for the program to jump back to the throw point.

  5. T F All type parameters defined in a function template must appear at least once in the function parameter list.

  6. T F The compiler creates an instance of a function template in memory as soon as it encounters the template.

  7. T F A class object passed to a function template must overload any operators used on the class object by the template.

  8. T F Only one generic type may be used with a template.

  9. T F In the function template definition, it is not necessary to use each type parameter declared in the template prefix.

  10. T F It is possible to overload two function templates.

  11. T F It is possible to overload a function template and an ordinary (nontemplate) function.

  12. T F A class template may not be derived from another class template.

  13. T F A class template may not be used as a base class.

  14. T F Specialized templates work with a specific data type.

Find the Error

Each of the following declarations or code segments has errors. Locate as many as possible.

  1. catch

    {

    quotient = divide(num1, num2);

    cout << "The quotient is " << quotient << endl;

    }

    try (string exceptionString)

    {

    cout << exceptionString;

    }

  2. try

    {

    quotient = divide(num1, num2);

    }

    cout << "The quotient is " << quotient << endl;

    catch (string exceptionString)

    {

    cout << exceptionString;

    }

  3. template <class T>

    T square(T number)

    {

    return T * T;

    }

  4. template <class T>

    int square(int number)

    {

    return number * number;

    }

  5. template <class T1, class T2>

    T1 sum(T1 x, T1 y)

    {

    return x + y;

    }

  6. Assume the following definition appears in a program that uses the SimpleVector class template presented in this chapter.

    int <SimpleVector> array(25);

  7. Assume the following statement appears in a program that has defined valueSet as an object of the SimpleVector class presented in this chapter. Assume valueSet is a vector of ints, and has 20 elements.

    cout << valueSet<int>[2] << endl;

Programming Challenges

  1. Date Exceptions

    Modify the Date class you wrote for Programming Challenge 1 of Chapter 13 (Date). The class should implement the following exception classes:

    InvalidDay Throw when an invalid day (< 1 or > 31) is passed to the class.
    InvalidMonth Throw when an invalid month (< 1 or > 12) is passed to the class.

    Demonstrate the class in a driver program.

  2. Time Format Exceptions

    Modify the MilTime class you created for Programming Challenge 4 of Chapter 15 (Time Format). The class should implement the following exceptions:

    BadHour Throw when an invalid hour (< 0 or > 2359) is passed to the class.
    BadSeconds Throw when an invalid number of seconds (< 0 or > 59) is passed to the class.

    Demonstrate the class in a driver program.

  3. Minimum/Maximum Templates

    Write templates for the two functions minimum and maximum. The minimum function should accept two arguments and return the value of the argument that is the lesser of the two. The maximum function should accept two arguments and return the value of the argument that is the greater of the two. Design a simple driver program that demonstrates the templates with various data types.

  4. Absolute Value Template

    Write a function template that accepts an argument and returns its absolute value. The absolute value of a number is its value with no sign. For example, the absolute value of -5 is 5, and the absolute value of 2 is 2. Test the template in a simple driver program.

  5. Total Template

    Write a template for a function called total. The function should keep a running total of values entered by the user, then return the total. The argument sent into the function should be the number of values the function is to read. Test the template in a simple driver program that sends values of various types as arguments and displays the results.

  6. IntArray Class Exception

    Chapter 14 presented an IntArray class that dynamically creates an array of integers and performs bounds checking on the array. If an invalid subscript is used with the class, it displays an error message and aborts the program. Modify the class so it throws an exception instead.

  7. TestScores Class

    Write a class named TestScores. The class constructor should accept an array of test scores as its argument. The class should have a member function that returns the average of the test scores. If any test score in the array is negative or greater than 100, the class should throw an exception. Demonstrate the class in a program.

  8. SimpleVector Modification

    Modify the SimpleVector class template presented in this chapter to include the member functions push_back and pop_back. The push_back function should accept an argument and insert its value at the end of the array. The pop_back function should accept no argument and remove the last element from the array. Test the class with a driver program.

  9. SearchableVector Modification

    Modify the SearchableVector class template presented in this chapter so it performs a binary search instead of a linear search. Test the template in a driver program.

  10. SortableVector Class Template

    Write a class template named SortableVector. The class should be derived from the SimpleVector class presented in this chapter. It should have a member function that sorts the array elements in ascending order. (Use the sorting algorithm of your choice.) Test the template in a driver program.

  11. Inheritance Modification

    Assuming you have completed Programming Challenges 9 and 10, modify the inheritance hierarchy of the SearchableVector class template so it is derived from the SortableVector class instead of the SimpleVector class. Implement a member function named sortAndSearch, both a sort and a binary search.

  12. Specialized Templates

    In this chapter, the section Specialized Templates within Section 16.4 describes how to design templates that are specialized for one particular data type. The section introduces a method for specializing a version of the SimpleVector class template so it will work with strings. Complete the specialization for both the SimpleVector and SearchableVector templates. Demonstrate them with a simple driver program.

  13. Exception Project

    This assignment assumes you have completed Programming Challenge 1 of Chapter 15 (Employee and ProductionWorker Classes). Modify the Employee and ProductionWorker classes so they throw exceptions when the following errors occur:

    • The Employee class should throw an exception named InvalidEmployeeNumber when it receives an employee number that is less than 0 or greater than 9999.

    • The ProductionWorker class should throw an exception named InvalidShift when it receives an invalid shift.

    • The ProductionWorker class should throw an exception named InvalidPayRate when it receives a negative number for the hourly pay rate.

    Write a driver program that demonstrates how each of these exception conditions works.

Solving the Exception Project Problem