throw ClassName(); // class constructor
A try block introduces a local scope and variables declared within a try block cannot be referred to outside the try block, including within the catch clauses.
Exception declaration of the catch clause can be single type or single object. An object should be declared when it is necessary to obtain the value or manipulate the exception object created by the throw expression.
catch (int) {
}
// or
catch (int i) {
}
By default it is a pass-by-value parameter, but can be changed to
reference declaration. The benefits are:
class Exception {
public:
Exception(int i) : value_(i) { }
int value() const { return value_; }
protected:
int value_;
};
try {
if (error)
throw Exception(error);
} catch (const Exception& e) {
cerr << "Exception number: " << e.value() << endl;
}
If no catch clause capable of handling the exception exists, program
execution resumes in the function terminate() defined
in C++ standard library, which default behavior is to call
abort().
returntype1 Function1(parametertype1 parameter) throw(string);
// error: exception specification omitted
returntype1 Function1(parametertype1 parameter)
{
// ...
}
class ClassName {
public:
void SetMember1(member1type member1) throw(InvalidParamException);
protected:
member1type member1_;
};
An empty exception specification guarantees that the function does
not throw any exception.
returntype1 Function2(parametertype1 parameter) throw();No type conversion is allowed between the type of the exception thrown and a type specified by the exception specification.
returntype1 Function1(parametertype1 parameter) throw(string)
{
if (error)
throw "Error!"; // run-time error: no const char* to string conversion
}
There is a small exception to this rule: when the exception
specification specifies a class type or a pointer to a class type.
If an exception specification specifies a class, then the function
may throw exception objects of a class publicly derived from the
class type in the exception specification. Similarly for pointers,
if an exception specification specifies a pointer to a class, the
function may throw exception objects that are pointers to a class
publicly derived from this class type.
class MathException : public Exception { };
returntype1 Function1(parametertype1 parameter) throw(Exception)
{
if (error)
throw MathException;
}
If the function throws exception that is not listed in its exception
specification (including the case that no conversion allowed) and
does not handles the exception before it "escapes" outside the
function, the compiler does not generate compile-time errors because
this violation can be detected only at run-time (program encounters
any exception can only known at run-time). Instead, the compiler
generates code that if the violation occurs, function
unexpected(), defined in the C++ standard run-time
library, is invoked. Its default behavior is to call
terminate().
try {
// possible throw type Exception1
} catch (Exception1) {
// ...
}
// ...
try {
// possible throw type Exception2
} catch (Exception2) {
// ...
}
Organization intermixes the handling of the exceptions with the normal
processing of the program is not ideal. After all, exceptions are
program anomalies that occur only in exceptional cases. Strategy that
separate the code handles the program anomalies from the code that
implements normal execution makes the code easier to follow and easier
to maintain.
try {
// possible throw type Exception1 and Exception2
} catch (Exception1) {
// ...
} catch (Exception2) {
// ...
}
Function try block is function that its entire body is contained
within the try block. This organization supports the cleanest
separation between normal processing code and exception handling
code.
int main()
try {
// ...
} catch (Exception1) {
// ...
} catch (Exception2) {
// ...
}
class Exception {
public:
static void print(string msg) const { cerr << msg << endl; }
}
class MathException : public Exception { };
class DivideByZeroException : public MathException {
// ...
}
Because the exception object is created by copying the
value of the throw expression, the exception thrown
always has the exact type of the expression specified on
the throw expression.
double Divide(int dividend, int divisor)
{
if (divisor == 0)
{
DivideByZeroException de(dividend, divisor);
MathException *pme = &de;
throw *pme; // exception object is type MathExecption
}
else
return dividend/(double)divisor;
}
The catch clause for the derived exception class must
appear first. This ensures that the catch clause for the
base class is only entered if no other catch clause
applies. User of a class library may choose the level of
granularity with which their applications will deal with
the exceptions thrown from the library.
try {
// ...
} catch (DivideByZeroException e) {
cerr << "Try to divide " << e.GetDividend() << " by "
<< e.GetDivisor() << endl;
} catch (Exception e) { // no need to deal with MathException
Exception::print("An exception encountered");
}
By default, the catch clause parameter resembles a
pass-by-value parameter. It creates its own local copy
which is initialized with base class subobject of the
actual derived class object. This copy is only accessed
within the catch clause and destroyed end of the clause.
If rethrown is the original exception object, not the
base class type local object. Recall also virtual
function only work for reference or pointer to base
class.
class Exception {
public:
virtual void print(void) const { cerr << "An exception encountered" << endl; }
}
class MathException : public Exception { };
class DivideByZeroException : public MathException {
public:
virtual void print(void) const { cerr << "Try to divide by xero" << endl; }
}
// usage in application
try {
// throw DivideByZeroException
} catch (Exception e) {
e.print(); // call Exception::print(void)
throw; // rethrow DivideByZeroException object
} // destroy local Exception object, but not DivideByZeroException object
If it is changed to a reference declaration to refer
directly to the derived class exception object created
by the throw expression (often for efficiency), virtual
function will work.
try {
// throw DivideByZeroException
} catch (const Exception& e) {
e.print(); // call DivideByZeroException::print(void)
throw; // rethrow DivideByZeroException object
} // no local Exception object to destroy
A virtual function in a base class may have an exception
specification that differs from the exception
specification of the member function that overrides it
in a derived class. However, the exception specification
of the derived class virtual function must be either
equally or more restrictive than the exception
specification of the base class virtual function.
This ensures that when the derived virtual function is
called through a pointer to a base class type, the call
is guaranteed not to violate the exception specification
of the base class member function.
class BaseName {
public:
virtual double F1(double) throw ();
virtual int F2(int) throw (int);
virtual string F3(void) throw (int, string);
};
class DerivedName : public BaseName {
public:
// error: exception specification less restrictive than BaseName::F1()
double F1(double) throw (string);
// ok: same exception specification as BaseName::F2()
int F2(int) throw (int);
// ok: derived F3() is more restrictive
string F3(void) throw (int);
};
Function try block becomes necessary with class
constructor, because normal try block inside the function
body does not enclose the member initialization list to
catch.
ClassName::ClassName(const char* name, double cent)
: dollar_(Divide(cent, CentPerDollar)) // may throw DivideByZeroException
try {
name_ = new char[strlen(name)+1];
} catch (DivideByZeroException) { // only possible using function try block
// ...
} catch (std::bad_alloc) { // try block inside function body can handle
// ...
}
C++ exception handling is nonresumptive; it doesn't provide a
primitive for returning to the point from which an exception was
thrown and continuing execution from there. If want to check to see
whether can fix a problem before throwing an exception, call a
function that checks and then throws only if the problem cannot be
dealt with locally, for example new_handler.
Basically, someone resuming from an exception handler can never be sure that the code after the point of throw was written to deal with the excecution just continuing as if nothing had happened. An exception handler cannot know how much context to "get right" before resuming. To get such code right, the writer of the throw and the writer of the catch need intimate knowledge of each others code and context. This creates a complicated mutual dependency that wherever it has been allowed has led to serious maintenance problems.
A throw expression behaves somewhat like a function call and the catch
clause behaves somewhat like a function definition. The main
difference between these exception handling and function call is that
all the information necessary to set up a function call is available
at compile-time. But exception handling requires run-time support.
Compiler does not know for a particular throw expression in which
function the catch clause resides and where execution resumes after
the exception has been handled. These decisions happen at run-time.
The compiler cannot inform users when no handler exists for an
exception. This is why the terminate() function exists:
it is a run-time mechanism to tell users when no handler matches
the exception thrown.
Throwing exception is not as fast as a normal function call. Exception handling is usually used to communicate program anomalies between parts of the program that are developed independently (for example throw from library layer and catch on user program). Error handling locally inside library may be better using error code. Exceptions are part of a library's interface and deciding which exceptions the library throws is an important phase of the library design.
Not all programs must catch exceptions thrown by libraries. Handling exception is useful when the user of a library cannot afford to crash, it gives the caller the choice as to which action should be taken when no meaningful action can be taken within the library code itself.
<exception>. Include
<stdexcept> to use them.
namespace std {
class exception {
public:
exception(void) throw();
exception(const exception&) throw();
exception& operator=(const exception&) throw();
virtual ~exception(void) throw();
virtual const char* what(void) const throw();
};
}
Since default constructor, copy constructor, copy assignment
operator and destructor are public, any program can freely
create, copy and assign exception objects. Virtual destructor
facilitates the definition of classes further derived from
the class. Virtual what() provide textual
description of the exception thrown and for overriding.
Empty exception specification guarantees that the member
function does not throw any exception again.
namespace std {
class logic_error : public exception {
public:
explicit logic_error(const string& what_arg);
};
class invalid_argument : public logic_error {
public:
explicit invalid_argument(const string& what_arg);
};
// receive argument that is not in the expected range of values
class out_of_range : public logic_error {
public:
explicit out_of_range(const string& what_arg);
};
// attempt to produce object with length that exceeds its
// maximal allowable size
class length_error : public logic_error {
public:
explicit length_error(const string& what_arg);
};
class domain_error : public logic_error {
public:
explicit domain_error(const string& what_arg);
};
}
namespace std {
class runtime_error : public exception {
public:
explicit runtime_error(const string& what_arg);
};
// range error in internal computation
class range_error : public runtime_error {
public:
explicit range_error(const string& what_arg);
};
// arithmetic overflow and underflow error
class overflow_error : public runtime_error {
public:
explicit overflow_error(const string& what_arg);
};
class underflow_error : public runtime_error {
public:
explicit underflow_error(const string& what_arg);
};
}
bad_alloc exception thrown by operator
new when it fails to allocate the
requested and bad_cast thrown when a
reference dynamic_cast fails.
namespace std {
class bad_alloc : public exception {
// ...
public:
bad_alloc(void) throw();
bad_alloc(const bad_alloc&) throw();
bad_alloc & operator=(const bad_alloc&) throw();
virtual ~bad_alloc(void) throw();
virtual const char* what(void) const throw();
};
}
Index