#include <iostream>

struct A
{
    A( int i ) : v(i)
    {
        if( v > 102 ) { std::cout << "\n*** throw ***\n\n" ; throw 100 ; }
        std::cout << "A::constructor v == " << v << " -- " ;
    }
    ~A() { std::cout << "A::destructor v == " << v << '\n' ; }

    const int v ;
};

struct B
{
    B( int i ) : a(i) { std::cout << "B::constructor: object at " << this << '\n' ; }
    ~B() { std::cout << "B::destructor: object at " << this << " -- " ; }

    A a ;
};

struct C
{
    C( int i ) : one(i), many { i+1, i+2, i+3, i+4 }
    { std::cout << "C::constructor\n" ; }

    ~C() { std::cout << "C::destructor\n" ; }

    B one ;
    B many[4] ;

    static void* operator new( std::size_t sz )
    { std::cout << "\n+++ C::operator new\n\n" ; return ::new char[sz] ; }

    static void operator delete( void* p )
    {
        std::cout << "\n+++ C::operator delete\n\n" ;
        return ::delete static_cast<char*>(p) ;
    }

    // ... new[], delete[]
};

int main()
{
    // no exceptions are thrown
    try { C c1(0) ; std::cout << "ok\n" ; }
    catch( int ) { std::cout << "constructor of c1 threw\n" ; }
    // objects constructed are destroyed in reverse order

    std::cout << "\n-------------------------------------\n" ;

    // constructor of C : first construct members (5 object of type B)
    //   each constructor of B : first construct members (an object of type A)
    //       constructor of A
    // after three objects of type B are created, C::one, C::many[0], C::many[1]
    // the constructor of the fourth B C::many[2] =>
    //                          constructor of A with i == 103 => which throws
    // the objects which had been constructed are destroyed in reverse order
    // of construction: C::many[1], C::many[0], C::one
    // (each of which destroys the contained sub-object of type A)
    try { C c2(100) ; std::cout << "ok\n" ; }
    catch( int ) { std::cout << "\n*** constructor of c2 threw ***\n" ; }

    std::cout << "\n-------------------------------------\n" ;

    // same as above, the storage duration does not make a difference
    // except that memory that was allocated by C::operator new
    // would be released by C::operator delete

    try { C* p = new C(100) ; std::cout << "ok\n" ; delete p ; }
    catch( int ) { std::cout << "\n*** constructor of C threw ***\n" ; }
}
