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


//=============================================