exception handling problem

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • asm23

    exception handling problem

    Hi, everyone, I'm learning <<thinking c++>volume Two, and testing the
    code below.

    /////////////////////////////////////////////////////////////////////////////
    //: C01:Wrapped.cpp
    #include <iostream>
    #include <cstddef>
    using namespace std;


    template<class T, int sz = 1class PWrap {
    T* ptr;
    public:
    class RangeError {}; // Exception class
    PWrap() {
    ptr = new T[sz];
    cout << "PWrap constructor" << endl;
    }
    ~PWrap() {
    delete[] ptr;
    cout << "PWrap destructor" << endl;
    }
    T& operator[](int i) throw(RangeErro r) {
    if(i >= 0 && i < sz) return ptr[i];
    throw RangeError();
    }
    };

    class Cat {
    public:
    Cat() { cout << "Cat()" << endl; }
    ~Cat() { cout << "~Cat()" << endl; }
    void g() {}
    };

    class Dog {
    public:
    void* operator new[](size_t) {
    cout << "Allocating a Dog" << endl;
    //throw 47; //*************** *************** *NOTE
    return 0;
    }
    void operator delete[](void* p) {
    cout << "Deallocati ng a Dog" << endl;
    ::delete[](p);
    }
    };

    class UseResources {
    PWrap<Cat, 3cats;
    PWrap<Dogdog;
    public:
    UseResources() {
    cout << "UseResources() " << endl;
    }
    ~UseResources() {
    cout << "~UseResources( )" << endl;
    }
    void f() { cats[1].g(); }
    };

    int main() {
    try {
    UseResources ur;
    } catch(int) {
    cout << "inside handler" << endl;
    } catch(...) {
    cout << "inside catch(...)" << endl;
    }
    }
    /////////////////////////////////////////////////////////////////////////////////
    In the UseResources class, It will create *Three cats* and *One dog*,
    When I comment the "throw 47" line in the class Dog implementation.
    it has the result output:
    =============== =============== ===
    Cat()
    Cat()
    Cat()
    PWrap constructor
    Allocating a Dog
    PWrap constructor
    UseResources()
    ~UseResources()
    Deallocating a Dog
    PWrap destructor
    ~Cat()
    ~Cat()
    ~Cat()
    PWrap destructor
    =============== =============== ====

    But When I uncomment the "throw 47" line, the result will be:

    =============== =============== ===
    Cat()
    Cat()
    Cat()
    PWrap constructor
    Allocating a Dog
    ~Cat()
    ~Cat()
    ~Cat()
    PWrap destructor
    inside handler
    =============== =============== ====

    It seems that an exception happened when we are create the Dog object.

    My Question:
    When an exception occurred in the allocate a Dog object,
    Why was *~UseResources( )* not called?
    Why was *~Cat()* called?

    How does the "exception mechanism" guarantee that the allocated object
    will be cleared.

    Someone can give this a brief trick? Is it about call stack unwinding?
    Thank you very much.



  • Paavo Helde

    #2
    Re: exception handling problem

    asm23 <asmwarrior@gma il.comkirjutas:
    Hi, everyone, I'm learning <<thinking c++>volume Two, and testing
    the code below.
    >
    ///////////////////////////////////////////////////////////////////////
    ////// //: C01:Wrapped.cpp
    #include <iostream>
    #include <cstddef>
    using namespace std;
    >
    >
    template<class T, int sz = 1class PWrap {
    T* ptr;
    public:
    class RangeError {}; // Exception class
    PWrap() {
    ptr = new T[sz];
    cout << "PWrap constructor" << endl;
    }
    ~PWrap() {
    delete[] ptr;
    cout << "PWrap destructor" << endl;
    }
    T& operator[](int i) throw(RangeErro r) {
    if(i >= 0 && i < sz) return ptr[i];
    throw RangeError();
    }
    };
    >
    class Cat {
    public:
    Cat() { cout << "Cat()" << endl; }
    ~Cat() { cout << "~Cat()" << endl; }
    void g() {}
    };
    >
    class Dog {
    public:
    void* operator new[](size_t) {
    cout << "Allocating a Dog" << endl;
    //throw 47; //*************** *************** *NOTE
    return 0;
    }
    void operator delete[](void* p) {
    cout << "Deallocati ng a Dog" << endl;
    ::delete[](p);
    }
    };
    >
    class UseResources {
    PWrap<Cat, 3cats;
    PWrap<Dogdog;
    public:
    UseResources() {
    cout << "UseResources() " << endl;
    }
    ~UseResources() {
    cout << "~UseResources( )" << endl;
    }
    void f() { cats[1].g(); }
    };
    >
    int main() {
    try {
    UseResources ur;
    } catch(int) {
    cout << "inside handler" << endl;
    } catch(...) {
    cout << "inside catch(...)" << endl;
    }
    }
    ///////////////////////////////////////////////////////////////////////
    ////////// In the UseResources class, It will create *Three cats* and
    *One dog*, When I comment the "throw 47" line in the class Dog
    implementation. it has the result output:
    =============== =============== ===
    Cat()
    Cat()
    Cat()
    PWrap constructor
    Allocating a Dog
    PWrap constructor
    UseResources()
    ~UseResources()
    Deallocating a Dog
    PWrap destructor
    ~Cat()
    ~Cat()
    ~Cat()
    PWrap destructor
    =============== =============== ====
    >
    But When I uncomment the "throw 47" line, the result will be:
    >
    =============== =============== ===
    Cat()
    Cat()
    Cat()
    PWrap constructor
    Allocating a Dog
    ~Cat()
    ~Cat()
    ~Cat()
    PWrap destructor
    inside handler
    =============== =============== ====
    >
    It seems that an exception happened when we are create the Dog object.
    >
    My Question:
    When an exception occurred in the allocate a Dog object,
    Why was *~UseResources( )* not called?
    Why was *~Cat()* called?
    In general, everything which has been constructed will be destructed.
    Something is considered constructed when its constructor completes. The
    Cats and their PWrap constructors were completed, so they were destructed
    as well. The Dog, its PWrap and UseResources constructors were not
    completed, so their destructors were not called.

    Note that because each your class wraps at most only one potentially
    failing resource allocation (a good design!), everything gets cleaned up
    automagically, no memory leaks etc.
    How does the "exception mechanism" guarantee that the allocated object
    will be cleared.
    It's the task for compiler writers, do not worry about this.
    >
    Someone can give this a brief trick? Is it about call stack unwinding?
    Thank you very much.
    Stack unwinding is about different objects in a stack frame, but here the
    issue is about subobjects of a complex object, which is slightly
    different.

    I would say this is more of the class invariants. The destructor is
    entitled to expect the object in a good shape, i.e. class invariant
    holding. If the constructor did not complete, there is no guarantee that
    the class invariant holds, thus the destructor cannot be called.

    hth
    Paavo



    Comment

    • asm23

      #3
      Re: exception handling problem

      Paavo Helde wrote:
      In general, everything which has been constructed will be destructed.
      Something is considered constructed when its constructor completes. The
      Cats and their PWrap constructors were completed, so they were destructed
      as well. The Dog, its PWrap and UseResources constructors were not
      completed, so their destructors were not called.
      >
      Note that because each your class wraps at most only one potentially
      failing resource allocation (a good design!), everything gets cleaned up
      automagically, no memory leaks etc.
      >
      >How does the "exception mechanism" guarantee that the allocated object
      >will be cleared.
      >
      It's the task for compiler writers, do not worry about this.
      >
      >Someone can give this a brief trick? Is it about call stack unwinding?
      >Thank you very much.
      >
      Stack unwinding is about different objects in a stack frame, but here the
      issue is about subobjects of a complex object, which is slightly
      different.
      >
      I would say this is more of the class invariants. The destructor is
      entitled to expect the object in a good shape, i.e. class invariant
      holding. If the constructor did not complete, there is no guarantee that
      the class invariant holds, thus the destructor cannot be called.
      >
      hth
      Paavo
      >
      >
      Hi Paavo, Thanks for helping me. The book also suggest that we could use
      a wrap class to avoid memory leak in class constructors. But I know,
      there are some tricks about the object creating. for example, once an
      constructor was executed, the state will be *remembered*, and then if
      exception happen after that, there objects in the try block frame will
      be recognized and their associated destructor will be called.

      I'm not quite understand about the term *class invariants* and *class
      invariant holding*. Does this mean the wrap class in my code?

      Thanks.

      Comment

      • Paavo Helde

        #4
        Re: exception handling problem

        asm23 <asmwarrior@gma il.comkirjutas:
        Paavo Helde wrote:
        >
        >In general, everything which has been constructed will be destructed.
        >Something is considered constructed when its constructor completes.
        >The Cats and their PWrap constructors were completed, so they were
        >destructed as well. The Dog, its PWrap and UseResources constructors
        >were not completed, so their destructors were not called.
        >>
        >Note that because each your class wraps at most only one potentially
        >failing resource allocation (a good design!), everything gets cleaned
        >up automagically, no memory leaks etc.
        >>
        >>How does the "exception mechanism" guarantee that the allocated
        >>object will be cleared.
        >>
        >It's the task for compiler writers, do not worry about this.
        >>
        >>Someone can give this a brief trick? Is it about call stack
        >>unwinding? Thank you very much.
        >>
        >Stack unwinding is about different objects in a stack frame, but here
        >the issue is about subobjects of a complex object, which is slightly
        >different.
        >>
        >I would say this is more of the class invariants. The destructor is
        >entitled to expect the object in a good shape, i.e. class invariant
        >holding. If the constructor did not complete, there is no guarantee
        >that the class invariant holds, thus the destructor cannot be called.
        >>
        >hth
        >Paavo
        >>
        >>
        >
        Hi Paavo, Thanks for helping me. The book also suggest that we could
        use a wrap class to avoid memory leak in class constructors. But I
        know, there are some tricks about the object creating. for example,
        once an constructor was executed, the state will be *remembered*, and
        then if exception happen after that, there objects in the try block
        frame will be recognized and their associated destructor will be
        called.
        IMO, a good design is such which is exception-safe without any try-catch
        blocks. C++ has built-in means (automatic call of destructors) to achieve
        this. The only thing which has to be followed is that a single
        constructor may not allocate more than 1 raw resource. For example, if
        there are two malloc calls in the contructor, then the first may succeed
        and the second fail; as the constructor does not complete, the destructor
        is not executed and the first resource leaks.

        To avoid this, the individual mallocs have to packaged in separate
        classes, either a home-made wrapper like in your case, or preferrably in
        an existing library class, like std::vector.

        See also: http://en.wikipedia.org/wiki/RAII
        >
        I'm not quite understand about the term *class invariants* and *class
        invariant holding*. Does this mean the wrap class in my code?


        regards
        Paavo

        Comment

        • asm23

          #5
          Re: exception handling problem

          Paavo Helde wrote:
          IMO, a good design is such which is exception-safe without any try-catch
          blocks. C++ has built-in means (automatic call of destructors) to achieve
          this. The only thing which has to be followed is that a single
          constructor may not allocate more than 1 raw resource. For example, if
          there are two malloc calls in the contructor, then the first may succeed
          and the second fail; as the constructor does not complete, the destructor
          is not executed and the first resource leaks.
          >
          To avoid this, the individual mallocs have to packaged in separate
          classes, either a home-made wrapper like in your case, or preferrably in
          an existing library class, like std::vector.
          >
          See also: http://en.wikipedia.org/wiki/RAII
          >
          >I'm not quite understand about the term *class invariants* and *class
          >invariant holding*. Does this mean the wrap class in my code?
          >

          >
          regards
          Paavo
          >
          Thanks Paavo for the second help.

          After carefully reading the RAII article on wikipedia, I gain a lot of
          knowledge about RAII. I'm feeling exciting. The exception-safe code is
          wonderful.

          The main *Idea* I know is "a scoped object is only destroyed if fully
          constructed. for example:
          foo(){
          CMyClass o1;
          CMyClass o2;
          CMyClass o3;
          }
          if an exception happened in o2's constructor, since o3 is not
          constructed, o3's destructor will never be called in exception handling.


          Now, I understand what the term *class invariant* means.


          Thanks for helping me, My question is totally solved.

          Comment

          • Paavo Helde

            #6
            Re: exception handling problem

            asm23 <asmwarrior@gma il.comkirjutas:
            >
            After carefully reading the RAII article on wikipedia, I gain a lot of
            knowledge about RAII. I'm feeling exciting. The exception-safe code is
            wonderful.
            Nice to hear! Coincidentally, this is what Bjarne Stroustrup says about
            himself in http://www.cilk.com/multicore-blog/b...ventor-Bjarne-
            Stroustrup-answers-the-Multicore-Proust-Questionnaire

            The contribution for which I most want to be remembered:
            The C++ destructor and the programming techniques that rely on it.

            Comment

            • asm23

              #7
              Re: exception handling problem

              Paavo Helde wrote:
              The contribution for which I most want to be remembered:
              The C++ destructor and the programming techniques that rely on it.
              >
              Yes, Thanks. I read the page.
              I think RAII is a big conception we should understand. Through I start
              to learned C++ about 4 years ago, I still lack some "big idea and
              conception" on this language. I'm writing code like "class and it's
              functions" day by day.

              Now I think there is more I should to learn in C++. Thanks for bring me
              on the right way.

              Comment

              Working...