| My Training Period: xx hours
21.8 The Exception Specifications
void test(int somecode) throw (bad_code, no_auth);
void (*PtrFunct)(double) throw(string, double);
void (*PtrFunct)(double) throw(string, double); … // more restrictive than PtrFunct: void One(double) throw (string); // as restrictive as PtrFunct: void Two(double) throw (string, double); // less restrictive than PtrFunct: void Three(double) throw (string, double, bool); PtrFunct = One; // OK PtrFunct = Two; // OK PtrFunct = Three; // error, Three is not subset of the PtrFunct
class Test { public: // may throw any exception int One(char *VarPtr); // doesn't throw any exception int Two(double *VarPtr1) throw(); }; |
Exception specifications are enforced at the runtime. When a function violates its exception specification,unexpected() function is called.
Theunexpected() function invokes a user-defined function that was previously registered by calling set_unexpected().
If no function was registered with set_unexpected(),unexpected() calls terminate() which aborts the program unconditionally.
The following table summarizes C++'s implementation of exception specifications:
Exception specification | Meaning |
throw() | The function does not throw any exception. |
throw(...) | The function can throw an exception. |
throw(type) | The function can throw an exception of type type. |
Table 21.2: Exception specification |
The following are examples of the exception specification implementation.
Example | Description |
void Funct() throw(int) | The function may throw an int exception. |
void Funct() throw() | The function will throw no exceptions. |
void Funct() throw(char*, T) | The function may throw a char* and/or a T, user defined type exception. |
void Funct() orvoid Funct(...) | The function may throw anything. |
Table 21.3: Exception specification example |
If exception handling is used in an application, there must be one or more functions that handle thrown exceptions.
Any functions called between the one that throws an exception and the one that handles the exception must be capable of throwing the exception. However, explicit exception specifications are not allowed on C functions.
using namespace std;
The extra overhead associated with the C++ exception handling mechanism may increase the size of executable files and slow your program execution.
So, exceptions should be used only in truly exceptional situations. Exception handlers should not be used to redirect the program's normal flow of control.
For example, an exception should not be thrown in cases of potential logic or user input errors, such as the overflow of an array boundary. In these cases, simply returning an error code by using for example, the conditional if statement may be simpler and more concise.
The C++ exception class serves as the base class for all exceptions thrown by certain expressions and by the Standard C++ Library.
Class hierarchy | Description |
exception |
|
bad_alloc | Thrown by new, an allocation request fails. |
bad_cast | Thrown by dynamic_cast whenfailed cast to a reference type. |
bad_exception | Thrown when an exception doesn't match any catch clause. |
bad_typeid | Thrown by typeid operator when the operand for typeid is a NULL pointer. |
The logical errors are normally caused by programmer mistakes. | |
logic_error | As the base class for all exceptions thrown to report errors presumably detectable before the program executes, such as violations of logical preconditions. |
domain_error | As the base class for all exceptions thrown to report a domain error. |
invalid_argument | As the base class for all exceptions thrown to report an invalid argument. |
length_error | As the base class for all exceptions thrown to report an attempt to generate an object too long to be specified. |
out_of_range | As the base class for all exceptions thrown to report an argument that is out of its valid range. |
The run-time errors normally occur because of mistakes in either the library functions or in the run-time system | |
runtime_error | As the base class for all exceptions thrown to report errors presumably detectable only when the program executes. |
overflow_error | As the base class for all exceptions thrown to report an arithmetic overflow. |
range_error | As the base class for all exceptions thrown to report a range error. |
underflow_error | As the base class for all exceptions thrown to report an arithmetic underflow. |
ios_base::failure | The member class serves as the base class for all exceptions thrown by the member function clear() in template class basic_ios. |
Table 21.4: exception class |
Logical and run time errors are defined in Standard C++ <stdexcept> header file and this <stdexcept> is a derived class from the exception class where the Standard C++ header file is<exception>.
Do not confuse with the exception class and <exception> header file, they refer to different thing here. Header is denoted by the angled bracket < >. Exception class definition is shown below:
class exception
{
public:
exception( ) throw( );
exception(constexception& right) throw( );
exception& operator=(const exception& right) throw( );
virtual ~exception( ) throw( );
virtual const char *what( ) const throw( );
};
|
// standard exceptions program example
#include <iostream>
#include<exception>
#include <typeinfo>
using namespace std;
class Test1
{ virtual Funct() { }; };
int main ()
{
try {
Test1 * var = NULL;
typeid (*var);
}
catch (std::exception& typevar)
{ cout<<"Exception: "<<typevar.what()<<endl; }
return 0;
}
Another code snippet example:
class out_of_range : public logic_error
{
public:
out_of_range(const string& message);
};
The value returned by what is a copy of message.
A program example:
Other<exception> header members are listed in the following tables.
typedef | Description |
terminate_handler | A type that describes a pointer to a function suitable for use as a terminate_handler. |
unexpected_handler | A type that describes a pointer to a function suitable for use as an unexpected_handler. |
Table 21.5: <exception> typedef |
Member function | Description |
set_terminate() | Establishes a new terminate_handler to be called at the termination of the program. |
set_unexpected() | Establishes a new unexpected_handler to be when an unexpected exception is encountered. |
terminate() | Calls a terminate handler. |
uncaught_exception() | Returns true only if a thrown exception is being currently processed. |
unexpected() | Calls an unexpected handler. |
Table 21.6: <exception> member function |
Class | Description |
bad_exception | The class describes an exception that can be thrown from an unexpected_handler. |
exception | The class serves as the base class for all exceptions thrown by certain expressions and by the Standard C++ Library. |
Table 21.7: <exception> class member |
Some simple program examples.
// bad_cast
// need to enable the Run-Time Type Info,
// rtti of your compiler. You will learn
//typecasting in another Module…
#include <typeinfo>
#include <iostream>
usingnamespace std;
class Myshape
{
public:
virtual void myvirtualfunc() const {}
};
class mytriangle: public Myshape
{
public:
virtual void myvirtualfunc() const
{ };
};
int main()
{
Myshape Myshape_instance;
Myshape &ref_Myshape = Myshape_instance;
try {
// try the run time typecasting, dynamic_cast
mytriangle &ref_mytriangle = dynamic_cast<mytriangle&>(ref_Myshape);
}
catch (bad_cast) {
cout<<"Can't do the dynamic_cast lor!!!"<<endl;
cout<<"Caught: bad_cast exception. Myshape is not mytriangle.\n";
}
return 0;
}
// bad_alloc, first version, when the allocation is OK
#include <new>
#include <iostream>
usingnamespace std;
int main()
{
char* ptr;
unsigned longint Test = sizeof(size_t(0)/3);
cout<<"The size of variable Test = "<<Test<<endl;
try
{
// try some allocation...
// size of an array must not exceed certain bytes
ptr = new char[size_t(0)/3]
delete[ ] ptr;
}
catch(bad_alloc &thebadallocation)
{
cout<<thebadallocation.what()<<endl;
};
return 0;
}
Let negate/inverse the array size, change the following
sizeof(size_t(0)/3)
To the following code.
sizeof(~size_t(0)/3)
Re-compile and re-run the program, the following output should be expected.
// set_unexpected
#include <exception>
#include <iostream>
usingnamespace std;
void myfunction()
{
cout<<"Testing myfunction()."<<endl;
//terminate() handler
terminate();
}
int main( )
{
unexpected_handler oldHandler = set_unexpected(myfunction);
// unexpected() function call
unexpected();
return 0;
}
Click the Abort button.
The following example shows the typeid operator throwing a bad_typeid exception.
// bad_typeid
#include <typeinfo>
#include <iostream>
usingnamespace std;
class Test
{
public:
// object for a class needs vtable for the rtti...
Test();
virtual ~Test();
};
int main()
{
Test *ptrvar = NULL;
try {
// the error condition
cout<<typeid(*ptrvar).name()<<endl;
}
catch (bad_typeid){
cout<<"The object is NULL"<<endl;
}
return 0;
}
// domain_error and typeid()
#include <iostream>
usingnamespace std;
int main()
{
try
{
throw domain_error("Some error with your domain!");
}
catch (exception &err)
{
cerr<<"Caught: "<<err.what()<<endl;
cerr<<"Type: "<<typeid(err).name()<<endl;
};
return 0;
}
// invalid_argument
#include <bitset>
#include <iostream>
usingnamespace std;
int main()
{
try
{
// binary wrongly represented by char X, a template based…
bitset<32> bitset(string("0101001X01010110000"));
}
catch (exception &err)
{
cerr<<"Caught "<<err.what()<<endl;
cerr<<"Type "<<typeid(err).name()<<endl;
};
return 0;
}
// runtime_error
#include <iostream>
usingnamespace std;
int main()
{
// runtime_error
try
{
locale testlocale("Something");
}
catch(exception &err)
{
cerr<<"Caught "<<err.what()<<endl;
cerr<<"Type "<<typeid(err).name()<<endl;
};
return 0;
}
// overflow_error, storage reserved is not enough
#include <bitset>
#include <iostream>
usingnamespace std;
int main()
{
try
{
// a template based…
bitset<100> bitset;
bitset[99] = 1;
bitset[0] = 1;
// to_ulong(), converts a bitset object to the integer
// that would generate the sequence of bits
unsigned long Test = bitset.to_ulong();
}
catch(exception &err)
{
cerr<<"Caught "<<err.what()<<endl;
cerr<<"Type "<<typeid(err).name()<<endl;
};
return 0;
}
// range_error
#include <iostream>
usingnamespace std;
int main()
{
try
{
throw range_error("Some error in the range!");
}
catch(exception &Test)
{
cerr<<"Caught: "<<Test.what()<<endl;
cerr<<"Type: "<<typeid(Test).name()<<endl;
};
return 0;
}
// underflow_error, the negative storage...
#include <iostream>
usingnamespace std;
int main()
{
try
{
throw underflow_error("The negative storage?");
}
catch(exception &Test)
{
cerr<<"Caught: "<<Test.what()<<endl;
cerr<<"Type: "<<typeid(Test).name()<<endl;
};
return 0;
}
Program example compiled usingg++.
// ***********-except.cpp-***********
// exception, class and destructor
#include <iostream>
using namespace std;
// a prototype...
void TestFunct(void);
// class Test1 declaration...
class Test1
{
public:
Test1(){ };
~Test1(){ };
const char *TestShow() const
{
cout<<"In class member function *TestShow():\n";
return " Exception in Test1 class.";
}
};
// another class declaration, DestrTest...
class DestrTest
{
public:
DestrTest();
~DestrTest();
};
// constructor class implementation
DestrTest::DestrTest()
{
cout<<"Next, in constructor DestrTest():\n";
cout<<" Constructing the DestrTest...\n";
}
// destructor class implementation
DestrTest::~DestrTest()
{
cout<<"Next, in destructor ~DestrTest():\n";
cout<<" Destructing the DestrTest...\n";
}
void TestFunct()
{
// instantiate an object, constructor invoked...
DestrTest p;
cout<<"Next in TestFunct(): \n Throwing Test1 type exception...\n";
// first throw...
throw Test1();
}
int main()
{
cout<<"Starting in main()...\n";
try
{
cout<<"Now, in the try block: \n Calling TestFunct()...\n";
TestFunct();
}
// instantiate another object, constructor invoked...
catch(Test1 q)
{
cout<<"Next, in catch handler:\n";
cout<<" Caught Test1 type exception...\n";
cout<<q.TestShow()<<"\n";
}
catch(char *strg)
{
cout<<"Caught char pointer type exception: "<<strg<<"\n";
}
cout<<"Back in main...\n";
return 0;
}
[bodo@bakawali ~]$ g++ except.cpp -o except
[bodo@bakawali ~]$ ./except
Starting in main()...
Now, in the try block:
Calling TestFunct()...
Next, in constructor DestrTest():
Constructing the DestrTest...
Next in TestFunct():
Throwing Test1 type exception...
Next, in destructor ~DestrTest():
Destructing the DestrTest...
Next, in catch handler:
Caught Test1 type exception...
In class member function *TestShow():
Exception in Test1 class.
Back in main...
tenouk fundamental of C++ object oriented tutorial
The source code for this tutorial is available inC/C++ Exception Handling source code.
Check the best selling C / C++, Object Oriented and pattern analysis books at Amazon.com.