Exceptions - Part 2


Throwing Simple Exceptions

We will be using this Stack class for the examples:
#include <iostream>

class IntStack1
{
  private:
    int *items_;    // The actual array of integers
    int size_;      // The number of integers currently on the stack
    int capacity_;  // The maximum number of integers the stack can hold
  public:
    IntStack1(int capacity = 10);
    ~IntStack1();
    void Push(int item);
    int Pop(void);
    bool IsEmpty(void) const;
    int GetCount(void) const;
    friend std::ostream & operator<<(std::ostream& os, const IntStack1& st);

      // Some values to indicate to the client what went wrong
    enum IntStack_EXCEPTION {E_PUSH_ON_FULL, E_POP_ON_EMPTY, E_NO_MEMORY};
};

The implementation of the IntStack1 class.


Example client code:

int main(void)
{
  IntStack1 st1(3);  // Create a stack to hold 3 items

  st1.Push(1);  // Ok
  st1.Push(2);  // Ok
  st1.Push(3);  // Ok, stack is full
  st1.Push(4);  // This will throw an exception

    // The remaining code never executes (including compiler-generated code)
  std::cout << st1;
  return 0;
}
Only some of the "expected" output is displayed:
In IntStack1 constructor: capacity = 3
before the "abnormal program termination" error message is generated (in Windows).

Notes:

  1. When an exception is thrown, statements code that follows the statment that threw the exception are skipped.
  2. Program execution is transferred to the catch block that handles the exception
  3. If no appropriate catch block is found, the terminate() function is called.
  4. By default, the terminate() function calls the abort() function, which ends the program immediately.
  5. Like many other things in C++, you can override the terminate() function to not call the abort() method. (See the set_terminate function.)

A more robust client:

int main(void)
{
  IntStack1 st1(3);  // Create a stack to hold 3 items

    // Protect code that can potentially throw an exception
  try 
  {
    st1.Push(1);  // Ok
    st1.Push(2);  // Ok
    st1.Push(3);  // Ok, stack is full
    st1.Push(4);  // This will throw an exception of type "IntStack_EXCEPTION"
  }
  catch (IntStack1::IntStack_EXCEPTION)
  {
    std::cout << "Caught IntStack_EXCEPTION" << std::endl;
  }
  
  std::cout << st1;
  return 0;
}
This outputs:
In IntStack1 constructor: capacity = 3
Caught IntStack_EXCEPTION
3
2
1
In IntStack1 destructor

Pushing and popping can both throw exceptions:

int main(void)
{
  IntStack1 st1(3);  // Create a stack to hold 3 items

  try // Protect code that can potentially throw an exception
  {
    st1.Push(1);  // Ok
    st1.Push(2);  // Ok
    st1.Push(3);  // Ok, stack is full
    st1.Push(4);  // This will throw an exception of type "IntStack_EXCEPTION"
    std::cout << st1.GetCount() << std::endl;  // This never executes
  }
  catch (IntStack1::IntStack_EXCEPTION e)
  {
    if (e == IntStack1::E_PUSH_ON_FULL)
      std::cout << "Can't push onto a full stack!" << std::endl;

    std::cout << st1 << std::endl;
  }

    // Unprotected code!
  st1.Pop();  // Ok
  st1.Pop();  // Ok
  st1.Pop();  // Ok, stack is empty
  st1.Pop();  // This will throw an exception of type "IntStack_EXCEPTION"

    // The remaining code never executes (including compiler-generated code)
  std::cout << st1.GetCount() << std::endl;  
  return 0;
}
Output before "abnormal program termination":
In IntStack1 constructor: capacity = 3
Can't push onto a full stack!
3
2
1

Handling Push and Pop:

int main(void)
{
  IntStack1 st1(3);  // Create a stack to hold 3 items

    // Protect code that can potentially throw an exception (PUSH_ON_FULL)
  try 
  {
    st1.Push(1);  // Ok
    st1.Push(2);  // Ok
    st1.Push(3);  // Ok, stack is full
    st1.Push(4);  // This will throw an exception of type "IntStack1_EXCEPTION"
    std::cout << st1.GetCount() << std::endl;  // This never executes
  }
  catch (IntStack1::IntStack_EXCEPTION e) // If an exception is thrown, handle it here
  {
    if (e == IntStack1::E_PUSH_ON_FULL)
      std::cout << "Can't push onto a full stack!" << std::endl;
  }

    // Protect code that can potentially throw an exception (POP_ON_EMPTY)
  try 
  {
    st1.Pop();  // Ok
    st1.Pop();  // Ok
    st1.Pop();  // Ok, stack is empty
    st1.Pop();  // This will throw an exception of type "IntStack1_EXCEPTION"
  }
  catch (IntStack1::IntStack_EXCEPTION e) // If an exception is thrown, handle it here
  {
    if (e == IntStack1::E_POP_ON_EMPTY)
      std::cout << "Can't pop from an empty stack!" << std::endl;
  }

  return 0;
}
Output:
In IntStack1 constructor: capacity = 3
Can't push onto a full stack!
Can't pop from an empty stack!
In IntStack1 destructor


Still haven't covered all the bases yet. The code below covers them all. Or does it?

int main(void)
{
    // Protect code that can potentially throw an exception
  try
  {
    IntStack1 st1(3);  // Create a stack to hold 3 items

    st1.Push(1);  // Ok
    st1.Push(2);  // Ok
    st1.Push(3);  // Ok, stack is full
    st1.Pop();    // Ok
    st1.Pop();    // Ok
    st1.Pop();    // Ok, stack is empty
    st1.Pop();    // This will throw an exception of type "IntStack1_EXCEPTION"
  }
  catch (IntStack1::IntStack_EXCEPTION e) // If an exception is thrown, handle it here
  {
    if (e == IntStack1::E_PUSH_ON_FULL)
      std::cout << "Can't push onto a full stack!" << std::endl;
    else if (e == IntStack1::E_POP_ON_EMPTY)
      std::cout << "Can't pop from an empty stack!" << std::endl;
    else if (e == IntStack1::E_NO_MEMORY)
      std::cout << "Can't allocate memory for stack!" << std::endl;
    else
      std::cout << "Something unexpected happened in the stack!" << std::endl;
  }

  return 0;
}
What happens in the above code if the call to new in the constructor of IntStack1 throws an exception?


"Someone" needs to catch what new is going to throw. Here's a modified catch block of the client:

catch (IntStack1::IntStack_EXCEPTION e) // If an exception is thrown, handle it here
{
  if (e == IntStack1::E_PUSH_ON_FULL)
    std::cout << "Can't push onto a full stack!" << std::endl;
  else if (e == IntStack1::E_POP_ON_EMPTY)
    std::cout << "Can't pop from an empty stack!" << std::endl;
  else
    std::cout << "Something unexpected happened in the stack!" << std::endl;
}
catch (const std::bad_alloc &e) // This is thrown from new
{
  std::cout << "Can't allocate memory for stack!" << std::endl;
}
or, we modify the IntStack1 class to catch it and throw what we initially expected:
IntStack1::IntStack1(int capacity) : size_(0), capacity_(capacity)
{
  std::cout << "In IntStack1 constructor: capacity = " << capacity << "\n"; 
  try {
    items_ = new int[capacity_];
  }
  catch (const std::bad_alloc &e) {
    throw E_NO_MEMORY;
  }
}


Catching everything:

int main(void)
{
    // Protect code that can potentially throw an exception
  try
  {
    IntStack1 st1(3);  // Create a stack to hold 3 items

    st1.Push(1);  
    st1.Pop();  
    st1.Pop();  // This will throw an exception of type "IntStack1_EXCEPTION"
  }
  catch (IntStack1::IntStack_EXCEPTION e) // If an exception is thrown, handle it here
  {
    if (e == IntStack1::E_PUSH_ON_FULL)
      std::cout << "Can't push onto a full stack!" << std::endl;
    else if (e == IntStack1::E_POP_ON_EMPTY)
      std::cout << "Can't pop from an empty stack!" << std::endl;
    else if (e == IntStack1::E_NO_MEMORY)
      std::cout << "Can't allocate memory for stack!" << std::endl;
    else
      std::cout << "Something unexpected happened in the stack!" << std::endl;
  }
  catch (...) // Catches anything
  {
    std::cout << "Something unexpected happened!" << std::endl;
  }

  return 0;
}


Can we rewrite the above catch blocks like this?

catch (IntStack1::E_PUSH_ON_FULL) // Catch full push
{
  std::cout << "Can't push onto a full stack!" << std::endl;
}
catch (IntStack1::E_POP_ON_EMPTY) // Catch empty pop
{
  std::cout << "Can't pop from an empty stack!" << std::endl;
}
catch (IntStack1::E_NO_MEMORY) // Catch out of memory
{
  std::cout << "Can't allocate memory for stack!" << std::endl;
}
catch (...) // Catch anything else
{
  std::cout << "Something unexpected happened!" << std::endl;
}


The try block represents a new scope:

int main(void)
{
  try 
  {
    IntStack1 st1(3);  // Create a stack to hold 3 items
  }
  catch (/* some type */) // Catch constructor exceptions
  {
    // Handle exceptions
  }

    // Protect for other exceptions
  try 
  {
    st1.Push(1);  // Error: st1 undefined
    // ...
  }
  catch (/* some type */)
  {
  }

  return 0;
}

Throwing Objects

Instead of throwing simple exceptions, we can throw objects. We'll use these classes:
  // Throw an object of this type when attempting
  // to push an item onto a full stack.
class PushOnFull
{ 
};

  // Throw an object of this type when attempting
  // to pop an item from an empty stack.
class PopOnEmpty
{
};
Don't be fooled. These are complete, working classes. (Of course, your definition of working may be different.)

Now, when we have an exception in the IntStack1 class, we'll instantiate an object from one of these classes and throw that.

Here's the modified Push and Pop methods:

void IntStack1::Push(int item)
{
  if (size_ == capacity_)
    throw PushOnFull();  // throw a PushOnFull object

  items_[size_++] = item;
}

int IntStack1::Pop(void)
{
  if (size_ == 0)
    throw PopOnEmpty(); // throw a PopOnEmpty object

  return items_[--size_];
}
And the corresponding client code:
int main(void)
{
  IntStack1 st1(3);  // Create a stack to hold 3 items

    // Protect code that can potentially throw an exception
  try
  {
    st1.Push(1);  
    st1.Pop();  
    st1.Pop();  // This will throw an exception of type "PopOnEmpty"
  }
  catch (const PushOnFull &e) // Catch push on full stack
  {
    std::cout << "Caught PushOnFull " << std::endl;
  }
  catch (const PopOnEmpty &e) // Catch pop from empty stack
  {
    std::cout << "Caught PopOnEmpty " << std::endl;
  }

  return 0;
}
Output:
In IntStack1 constructor: capacity = 3
Caught PopOnEmpty
In IntStack1 destructor
Points:
Look carefully again at how the exception object is caught:
catch (const PopOnEmpty &e) // Catch pop from empty stack
Then look again at how (where) it is constructed. We can rewrite it this way to underscore an important point:
int IntStack1::Pop(void)
{
  if (size_ == 0)
  {
    PopOnEmpty temp; // Construct the object
    throw temp;      // Throw it
  }

  return items_[--size_];
}
Something about this should alarm you. How can this work correctly? Throwing exceptions created on the stack are safe:

Design tip Catching exceptions by reference will limit the number of times the exception object is copied.


Embellishing the Exception

We can add as much information to our exception object as we like. So, let's include the value of the item that caused the exception. The modified PushOnFull class looks like this:
  // Throw an object of this type when attempting
  // to push an item onto a full stack.
class PushOnFull
{ 
  public:
      // Construct the object with the value that
      // was attempting to be pushed.
    PushOnFull(int value) : value_(value)
    {
    }

    int GetValue(void) const
    {
      return value_;
    }
  private:
    int value_;
};
The modified Push method:
void IntStack1::Push(int item)
{
  if (size_ == capacity_)
    throw PushOnFull(item);  // throw a PushOnFull object including the offender

  items_[size_++] = item;
}
A snippet from the client:
try
{
  st1.Push(1);  
  st1.Push(2);  
  st1.Push(3);  
  st1.Push(4); // This will throw an exception of type "PushOnFull"
}
catch (const PushOnFull &e) // Catch push on full stack and examine the object
{
  std::cout << "Caught exception trying to push " << e.GetValue() << std::endl;
}
Output:
In IntStack1 constructor: capacity = 3
Caught exception trying to push 4
In IntStack1 destructor

Standard Exception Classes

All exceptions thrown from C++ (or the library) are derived from exception.
  1. Language exceptions
  2. Library exceptions
  3. External exceptions

GNU Documentation


The exception Class

The interface for the standard exception class in the C++ library looks like this:
namespace std {
  class exception
  {
    public:
      exception() throw();
      exception(const exception&) throw();
      exception& operator=(const exception&) throw();
      virtual ~exception() throw();
      virtual const char *what() const throw();
  };
}

The throw() keyword after the function declaration is called an exception specification and lets the world know what types of exceptions might be thrown. The empty parentheses means that this function promises not to throw any exceptions. More on exception specifications later.

Throwing exceptions of type std::exception:

#include <exception>  // need to include this for the exception classes

void IntStack1::Push(int item)
{
  if (size_ == capacity_)
    throw std::exception();  // throw an exception object

  items_[size_++] = item;
}

int IntStack1::Pop(void)
{
  if (size_ == 0)
    throw std::exception();  // throw an exception object

  return items_[--size_];
}
We will also have to modify the client:
int main(void)
{
  IntStack1 st1(3);  // Create a stack to hold 3 items

    // Protect code that can potentially throw an exception
  try
  {
    st1.Push(1);  
    st1.Pop();  
    st1.Pop();  // This will throw an exception of type "exception"
  }
  catch (const std::exception &e) // If an exception is thrown, handle it here
  {
    std::cout << "Caught exception: " << e.what() << std::endl;
  }

  return 0;
}
Output:
In IntStack1 constructor: capacity = 3
Caught exception: Unknown exception
In IntStack1 destructor
Notes:

Deriving Classes from exception

The exception class doesn't provide much functionality. It does, however, provide an interface as it is designed to be used as a base class for our own exception classes. (Note the virtual methods.)

  // Throw an object of this type when attempting
  // to push an item onto a full stack.
class PushOnFull : public std::exception
{ 
  public:
    PushOnFull(int value, const std::string &message = "Pushing on full stack") : value_(value), message_(message)
    {
    }

    int GetValue(void) const
    {
      return value_;
    }

      // Override virtual method with our message (instead of "Unknown exception")
    virtual const char *what(void) const throw()
    {
      return message_.c_str();
    }

  private:
    int value_;
    std::string message_;
};

  // Throw an object of this type when attempting
  // to pop an item from an empty stack.
class PopOnEmpty : public std::exception
{
  public:
    PopOnEmpty(const std::string &message = "Popping from empty stack") : message_(message)
    {
    }
      // Override virtual method with our message (instead of "Unknown exception")
    virtual const char *what(void) const throw()
    {
      return message_.c_str();
    }

  private:
    std::string message_;
};
Now, when our class throws either of these exceptions:
throw PushOnFull(item);
throw PopOnEmpty();
The client catches them and calls the what() method on the exception object:
catch (const PushOnFull &e) // Catch push on full stack
{
  std::cout << e.what() << std::endl;
}

catch (const PopOnEmpty &e) // Catch pop from empty stack
{
  std::cout << e.what() << std::endl;
}
they will see this:
Pushing on full stack
Popping from empty stack
Notice that we could have caught either exception using the base class in the catch block:
catch (const std::exception &e) // Catch either exception
{
  std::cout << e.what() << std::endl;
}
This is the reason for deriving from a common base class: we can call the common methods (what()).


By having the Push() and Pop() methods provide different messages when they throw their respective exceptions, we can get more specific information:

  // In IntStack::Push(int item)
throw PushOnFull(item, "What? Are you mad?!?!?! The stack is full already!");

  // In IntStack::Pop()
throw PopOnEmpty("What word in 'The stack is empty' don't you understand?!?!");
or, for something that's actually useful (like including the filename and line number where the exception occurred):
void IntStack1::Push(int item)
{
  if (size_ == capacity_)
  {
    char msg[256];
    sprintf(msg, "Trying to push %i in function IntStack1::Push() at line %i\nin file: %s\n\n", item, __LINE__, __FILE__);
    throw PushOnFull(item, msg); 
  }
  items_[size_++] = item;
}

int IntStack1::Pop(void)
{
  if (size_ == 0)
  {
    char msg[256];
    sprintf(msg, "In function IntStack1::Pop() at line %i\nin file: %s\n\n", __LINE__, __FILE__);
    throw PopOnEmpty(msg); 
  }
  return items_[--size_];
}
Now the client can catch these as before:
catch (const PushOnFull &e) // Catch push on full stack
{
  std::cout << e.what() << std::endl;
}

catch (const PopOnEmpty &e) // Catch pop from empty stack
{
  std::cout << e.what() << std::endl;
}
or perhaps with a single catch block:
catch (const std::exception &e) // Catch either exception
{
  std::cout << e.what() << std::endl;
}
The client sees this when a PushOnFull exception is thrown:
Trying to push 4 in function IntStack1::Push() at line 69
in file: E:\DigiPen\Courses\CS270\Code\Sandbox\Exceptions\IntStack1.cpp
And sees this when a PopOnEmpty exception is thrown:
In function IntStack1::Pop() at line 81
in file: E:\DigiPen\Courses\CS270\Code\Sandbox\Exceptions\IntStack1.cpp


Since we are likely to have a lot of common code in all of our exceptions coming from the IntStack class, we can put that functionality in a common base class:

class StackException : public std::exception
{
  public:
      // Generic stack exception
    StackException(const std::string &message = "Unknown StackException") : message_(message)
    {
    }

    virtual const char *what(void) const throw()
    {
      return message_.c_str();
    }

    virtual ~StackException()
    {
    }

  private:
    std::string message_;
};
And we would derive our specific stack exceptions from the more general base class:
  // Throw an object of this type when attempting
  // to push an item onto a full stack.
class PushOnFull : public StackException
{
  public:
      // The string has a default (generic "full stack" message)
    PushOnFull(int value, const std::string &message = "Pushing on full stack") : StackException(message), value_(value)
    {
    }

    int GetValue(void) const
    {
      return value_;
    }

  private:
    int value_;
};

  // Throw an object of this type when attempting
  // to pop an item from an empty stack.
class PopOnEmpty : public StackException
{
  public:
      // The string has a default (generic "empty stack" message)
    PopOnEmpty(const std::string &message = "Popping from empty stack") : StackException(message)
    {
    }
};
One important benefit of deriving from class exception is that you will be guaranteed to catch the exception object and call its what () method:
int main(void)
{
  IntStack1 st1(3);  // Create a stack to hold 3 items

    // Protect code that can potentially throw an exception
  try
  {
    // use the stack ...
  }
  catch (const PushOnFull &e) // Catch push on full stack
  {
    std::cout << e.what() << ": " << e.GetValue() << std::endl;
  }
  catch (const PopOnEmpty &e) // Catch pop from empty stack
  {
    std::cout << e.what() << std::endl;
  }
  catch (const StackException &e) // Catch "generic" StackException
  {
    std::cout << e.what() << std::endl;
  }
  catch (const exception &e) // Catch "generic" exception
  {
    std::cout << e.what() << std::endl;
  }
  catch (...) // Catch anything else
  {
    std::cout << "Something bad and unexpected happened" << std::endl;
  }

  return 0;
}
Also, the order of the catch blocks is important. Given the flawed code below, the catch blocks for PushOnFull, PopOnEmpty, and StackException will never be executed.
int main(void)
{
  IntStack1 st1(3);  // Create a stack to hold 3 items

    // Protect code that can potentially throw an exception
  try
  {
    // use the stack ...
  }
  catch (const exception &e) // Catch "generic" exception
  {
    std::cout << e.what() << std::endl;
  }
  catch (const PushOnFull &e) // Catch push on full stack
  {
    std::cout << e.what() << ": " << e.GetValue() << std::endl;
  }
  catch (const PopOnEmpty &e) // Catch pop from empty stack
  {
    std::cout << e.what() << std::endl;
  }
  catch (const StackException &e) // Catch "generic" StackException
  {
    std::cout << e.what() << std::endl;
  }
  catch (...) // Catch anything else
  {
    std::cout << "Something bad and unexpected happened" << std::endl;
  }

  return 0;
}
Fortunately, many compilers can catch this obvious programmer error and issue a warning. This is what Microsoft's compiler says:
warning C4286: 'class PushOnFull &' : is caught by base class ('class exception &') on line 128
warning C4286: 'class PopOnEmpty &' : is caught by base class ('class exception &') on line 128
warning C4286: 'class StackException &' : is caught by base class ('class exception &') on line 128

Exception Specifications

Recall the standard exception class:
namespace std {
  class exception
  {
    public:
      exception() throw();
      exception(const exception&) throw();
      exception& operator=(const exception&) throw();
      virtual ~exception() throw();
      virtual const char *what() const throw();
  };
}
The trailing throw() keywords are called exception specifications. The syntax for the specification is:
throw(<exception_type1>, <exception_type2>, <exception_type3>, etc.)
where the parentheses surround a comma-separated list of exception types. Exception specifications indicate a few things: Three different exception specifications for the Push method:
void IntStack1::Push(int item) throw(PushOnFull)
{
  if (size_ == capacity_)
  {
    throw PushOnFull(item); // OK, obvious
    throw PopOnEmpty();     // Incorrect, PopOnEmpty is not a PushOnFull 
    throw StackException(); // Incorrect, StackException is not a PushOnFull
    throw exception();      // Incorrect, exception is not a PushOnFull
  }
  items_[size_++] = item;
}

void IntStack1::Push(int item) throw(StackException)
{
  if (size_ == capacity_)
  {
    throw PushOnFull(item); // OK, PushOnFull is a StackException
    throw PopOnEmpty();     // OK (but doesn't make sense), PopOnEmpty is a StackException
    throw StackException(); // OK, obvious
    throw exception();      // Incorrect
  }
  items_[size_++] = item;
}

void IntStack1::Push(int item) throw(std::exception)
{
  if (size_ == capacity_)
  {
    throw PushOnFull(item); // OK, PushOnFull is a std::exception
    throw PopOnEmpty();     // OK (but doesn't make sense), PopOnEmpty is a std::exception
    throw StackException(); // OK, StackException is a std::exception
    throw exception();      // OK, obvious
    throw ("Hello");        // Incorrect, const char * is not a std::exception
  }
  items_[size_++] = item;
}
Unfortunately, violating the exception specification by throwing incompatible exceptions are not caught by Microsoft's VC++ 6.0 compiler. Borland's compiler does catch the offense and issues a warning:
Warning W8078 IntStack1.cpp 71: Throw expression violates exception specification in function IntStack1::Push(int) throw(PushOnFull)
Consider this code below. What happens at runtime?
void ThrowDouble(void) throw(double)
{
  throw 123.0;  // 123.0 is a double
}

int main(void)
{
  try {
    ThrowDouble();
  } 
  catch (double d) {
    std::cout << "Caught double: " << d << std::endl;
  }
  catch (...) {
    std::cout << "Caught something else...\n";
  }
  return 0;
}
Now, change the ThrowDouble function to throw an int instead:
void ThrowDouble(void) throw(double)
{
  throw 123;  // 123 is an int
}
What might happen at runtime? Notes: A potentially bigger note:

Microsoft's 7.1 compiler ignores exception specifications. In fact, it generates a warning if it encounters one. The warning is a message that simply states that the exception specification will be ignored. That's why the code above will print "Caught something else..." instead of terminating the program (which is what compliant compilers will do).

One last note: Using GNU's compiler under Windows, you might see something like this:

6 [sig] a 2564 open_stackdumpfile: Dumping stack trace to a.exe.stackdump
where a.exe.stackdump is a file containing a, well, stack dump that looks like this:
Stack trace:
Frame     Function  Args
0022FD58  61073F0A  (00000A04, 00000006, 0022FDB8, 61076201)
0022FDA8  61074122  (00000A04, 00000006, 0022FDF8, 6107465A)
0022FDB8  61073FCC  (00000006, 00000006, 0022FE90, 004010C7)
0022FDF8  6107465A  (0A042B58, 00000000, 0022FE88, 00426503)
0022FE08  004062CD  (00405230, 00000000, 00000000, 00000000)
0022FE88  00426503  (0A042B88, 00429A24, 00000000, 610078FB)
0022FEA8  004010E5  (0000000B, 00000354, 7C4E91AA, 00000001)
0022FEE0  00401140  (00000001, 0A042AE0, 0A040328, 00000001)
0022FF40  61007408  (610D1F58, FFFFFFFE, 000003D8, 610D1E7C)
0022FF90  610076ED  (00000000, 00000000, 8043138F, 00000000)
0022FFB0  004051F2  (0040111C, 037F0009, 0022FFF0, 7C4E87F5)
0022FFC0  0040103C  (00000200, 0012F758, 7FFDF000, FFFFFFFF)
0022FFF0  7C4E87F5  (00401000, 00000000, 000000C8, 00000100)
End of stack trace

Rethrowing Exceptions

Sometimes you catch an exception, but then decide that you want to "pass it on" to another handler. This is done by rethrowing the exception. What is printed by the program below?
void f2(void)
{
  IntStack1 st1(2);
  st1.Push(1); st1.Push(2);
  st1.Push(3);  // This will throw a PushOnFull exception
}

void f1(void)
{
  try 
  {
    f2();
  }
  catch (const std::exception &e) // Catch any kind of exception-derived exception
  {
    std::cout << "In f1: Caught exception: " << e.what() << std::endl;
    throw;  // re-throw the original exception (PushOnFull exception)
  }
}

int main(void)
{
  try {
    f1();
  } 
  catch (const PushOnFull &e) {
    std::cout << "In main: Caught PushOnFull with value:  " << e.GetValue() << std::endl;
  }
  catch (const std::exception &e) {
    std::cout << "In main: Caught exception:  " << e.what() << std::endl;
  }
  return 0;
}
This is the output:
In f1: Caught exception: Pushing on full stack
In main: Caught PushOnFull with value: 3
Now, modify the function to throw the exception that was caught:
void f1(void)
{
  try 
  {
    f2();
  }
  catch (const std::exception &e) // Catch any kind of exception-derived exception
  {
    std::cout << "In f1: Caught exception: " << e.what() << std::endl;
    throw e;  // throw a new exception of type std::exception (PushOnFull is lost)
  }
}
And this is what we get:
In f1: Caught exception: Pushing on full stack
In main: Caught exception: Unknown exception
Notes:

Function try Blocks

Suppose we want to protect all of the code in a function. We'd have to wrap the code in the body in a try..catch block:
f(void)
{
  try {
    MathStuff maths();
    maths.DoSomeMath();
    // maybe more code to do math stuff
    //   an exception gets thrown here
    // ...
  }
  catch (const EDivideByZero &e) {
    // deal with divide by zero
  }
  catch (const EOverflow &e) {
    // deal with overflow
  }
  catch (...) {
    // deal with other exception
  }
  return 0;
}
If you want to really separate the "normal" code from the "exceptional" code in a function, wrap the entire body of the function in a try..catch block:
int f(void)
try
{
  MathStuff maths();
  maths.DoSomeMath();
  // maybe more code to do math stuff
  //   an exception gets thrown here
  // ...
  return 0;
}
catch (const EDivideByZero &e) 
{
  // deal with divide by zero
}
catch (const EOverflow &e) 
{
  // deal with overflow
}
catch (...) 
{
  // deal with other exception
}
This is possible for main as well:
int main(void)
try {
  IntStack1 st1(3);  // Create a stack to hold 3 items

  st1.Push(1);  
  st1.Push(2);  
  st1.Push(3);  
  st1.Push(4); // This will throw an exception of type "PushOnFull"
  return 0;
}
catch (const PushOnFull &e) // Catch push on full stack
{
  std::cout << "Caught exception trying to push " << e.GetValue() << std::endl;
}
catch (const PopOnEmpty &e) // Catch pop from empty stack
{
  std::cout << "Caught PopOnEmpty " << std::endl;
}
Output:
In IntStack1 constructor: capacity = 3
In IntStack1 destructor
Caught exception trying to push 4
Suppose we want to catch the exception in the constructor? We need to modify the constructor to use a function try block:
int Val(void)
{
  throw "Oops";
}

class T
{
  public:
    T() try 
      : a_( Val() ) { 
      std::cout << "In T constructor\n";

    }
    catch (const char *) {
      std::cout << "Caught exception in T constructor\n";
    }

    ~T() {
      std::cout << "In T destructor\n";
    }
  private:
    int a_;
};
We can run this program to see what happens:

int main(void) 
{
  T t;  // this is gonna throw
  std::cout << "In main...\n";
  return 0;
}
The message from the catch block is printed just before the program crashes.
Caught exception in T constructor
There are some sticky details here: