Constructors
Constructors have no return value. Still, a constructor sometimes must
propagate the failure. There are several method for this: error flags,
like in ios_base, globals, like errno in C, and most importantly exceptions.
However exceptions in constructors have special behaviour.
// error detection with member:
void f()
{
ifstream inp( "input.dat" );
if ( ! inp ) ...
...
}
//==============================================
// use exceptions for constructors
class X;
int main()
{
try
{
X *xp = new X();
}
catch( ...) { /* space allocated, but xp was not set */ }
// still no problem: system deallocate space
}
// but:
class X
{
public:
X(int i) { p = char[i]; init(); }
~X() { delete [] p; } // must not throw exception
private:
void init() { ... throw ... } // BAD: destructor won't run !
char *p; // constructor was not completed
};
Member initialization
class Y
{
public:
Y(int i, int j) : x(i), z(j) { } // x(i) or z(j) throws exception
// but Y must not emit any
private:
X x;
Z z;
};
class Y
{
public:
Y(int i, int j)
try
: x(i), z(j) // x(i) or z(j) throws exception
{ }
catch( ... )
{
// we get here if either x(i) or z(j) throws exception
// if x(i) throws, then z uninitialized
// if z(j) throws, then ~X::X() has already executed
// what to do here ??
}
private:
X x;
};
#include <iostream>
class X
{
public:
X() { throw 1; }
};
class Y
{
public:
Y()
try
: x()
{ }
catch( ... ) { /* throw; */ }
private:
X x;
};
int main()
try {
Y y;
return 0;
}
catch (int i)
{
std::cerr << "exception: " << i << std::endl;
}
// if a subobject initializer throws exception : thet subobject has
// not been created: no object without subobject, so constructor
// will throw exception !
Destructors
The rule of thumb for exceptions in destructor: they must never throw.
Desctructor can be called in one of two ways:
- normal mode
- call during exception handling
in the later emitting an exception cause undefined behaviour most likely
teh call of terminate()
~X::X() throw()
try
{
...
}
catch( ... ) { ... }
~T::T()
{
if ( ! uncaught_exception() )
{
// code that could throw...
}
else
{
// code must not throw
}
}
Exception specification
In Java exception specification is a mandatory part of method declaration.
In C++ exception specification is optional. When it is used, the behaviour
is different, than in Java.
class E1;
class E2;
void f() throw(E1) // throws only E1 or subclasses
{
...
throw E1(); // throws exception of type E1
...
throw E2(); // calls unexpected() which calls terminate()
}
// same as:
void f()
try {
...
}
catch(E1) { throw; }
catch(...) { std::unexpected(); }
//============================================
class E1;
class E2;
void f() throw(E1,std::bad_exception) // throws only E1 or subclasses
{ // or bad_exception
...
throw E1(); // throws exception of type E1
...
throw E2(); // calls unexpected() which throws bad_exception
}
typedef void (*unexpected_handler)();
unexpected_handler set_unexpected(unexpected_handler);
typedef void (*terminate_handler)();
terminate_handler set_terminate(terminate_handler);
// terminate() by default calls abort()
void f() throw() { } // can improve efficiency
//=============================================