ctor/dtor calls and ctor init seq

Discussion in 'C++' started by Grizlyk, Nov 25, 2006.

  1. Grizlyk

    Grizlyk Guest

    Good morning.

    Look here:
    http://groups.google.com/group/fido7.ru.cpp.chainik/browse_frm/thread/7341aba5238c0f79
    and here:
    http://groups.google.com/group/fido7.ru.cpp.chainik/browse_frm/thread/cb014f4ba9df614a

    Can anybody answer?

    Who can't read in russian:

    ....
    struct T
    {
    int *a;
    T1 b;
    T2 c;

    void print()const throw();

    T()throw(exception&):a(0),b(0){ a=new int[10]; b...=a;}
    ~T()throw(){ delete[] a; a=0; }
    };
    ....
    try{ T obj; obj.b.input(); obj.print(); }catch(...){}
    ....

    Q1: ctor throw exception during init, for ex
    T()throw(exception&):
    a(0),
    b(0), //throw here
    /*c()*/
    T::ctor user code (in {}) don't start exec. Will T::dtor be called or
    no?

    Q2: ctor throw exception after init, for ex
    T()throw(exception&):a(0), b(0), /*c()*/
    {
    a=new int[10]; //throw here
    b...=a;
    }
    T::ctor user code (in {}) is partial (not complete) executed. Will
    T::dtor be called or no?

    Q3: What sequence of class-members, base class and virtual base class
    is used for init:
    a)seq of appearence in
    list of initializers
    list of inheritance
    class description
    b) indefinite order

    Answers must be based on appropriate parts of C++ std rules (publish
    complete list, pls:).
    Grizlyk, Nov 25, 2006
    #1
    1. Advertising

  2. * Grizlyk:
    > Good morning.

    [snip]
    >
    > Q3: What sequence of class-members, base class and virtual base class
    > is used for init:
    > a)seq of appearence in
    > list of initializers
    > list of inheritance
    > class description
    > b) indefinite order
    >
    > Answers must be based on appropriate parts of C++ std rules (publish
    > complete list, pls:).


    Please do your HOMEWORK on your own.

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?
    Alf P. Steinbach, Nov 25, 2006
    #2
    1. Advertising

  3. Grizlyk

    Grizlyk Guest

    Good morning.

    >Alf P. Steinbach:
    > Please do your HOMEWORK on your own.

    The local Pinkerton finds out my real desires - no hide, no run from
    him!

    Are anybody think another about my question? What is it, even Bazarov
    keep silence for subj? Some years ago he wrote certainly something
    about _ÓÔÁÎÄÁÒÔ_.
    Grizlyk, Nov 26, 2006
    #3
  4. Grizlyk

    David Harmon Guest

    On 25 Nov 2006 20:06:39 -0800 in comp.lang.c++, "Grizlyk"
    <> wrote,
    >
    >Are anybody think another about my question? What is it, even Bazarov
    >keep silence for subj? Some years ago he wrote certainly something
    >about _ÓÔÁÎÄÁÒÔ_.


    This issue is covered in Marshall Cline's C++ FAQ. See the topic
    "[17.4] How should I handle resources if my constructors may throw
    exceptions?" It is always good to check the FAQ before posting. You
    can get the FAQ at:
    http://www.parashift.com/c -faq-lite/
    David Harmon, Nov 27, 2006
    #4
  5. Grizlyk

    Grizlyk Guest

    > David Harmon ÐÉÓÁÌ(Á):
    > This issue is covered in Marshall Cline's C++ FAQ. See the topic
    > "[17.4] How should I handle resources if my constructors may throw
    > exceptions?" It is always good to check the FAQ before posting. You
    > can get the FAQ at:
    > http://www.parashift.com/c -faq-lite/


    "It is always good to check the FAQ before posting." You are right "in
    general", but not right for the qwestion. The answer for this is always
    short and easy for anybody, who can write C++ more then copying FAQ
    examples and more then filling ready class skeleton and who can explain
    at least self programs.

    It is absolutly unclear why "If a constructor throws an exception, the
    object's destructor is not run." One can imagin too diffrent parts of
    ctor of any class, they looks like
    1. list of initializers
    2. code in {}
    And there is absolutely no sence to treat exception from them equally.

    I think, the nesgroup is group of very busy men. It is better to me to
    find other :)
    Grizlyk, Nov 28, 2006
    #5
  6. Grizlyk

    David Harmon Guest

    On 27 Nov 2006 20:16:20 -0800 in comp.lang.c++, "Grizlyk"
    <> wrote,
    >It is absolutly unclear why "If a constructor throws an exception, the
    >object's destructor is not run."


    If the constructor throws, the object is not completely constructed.
    The destructor has no way to know that. A non-trivial destructor would
    have no way to know what it needs to do.

    If the constructor is going to throw, it should first clean up the
    _part_ of the class it has built so far.

    >One can imagin too diffrent parts of
    >ctor of any class, they looks like
    >1. list of initializers


    Base classes and members with constructors should have their own
    constructors. If some base classes and members have been constructed
    when one of them throws, the destructors of the ones already constructed
    are automatically run in the reverse order of construction.

    >2. code in {}


    There is where the "if the constructor throws" really applies. Again,
    base classes and members with destructors take care of themselves, so
    what you really need to look after there is unfinished business of the
    {} code itself. With good RAII design there will not be too much of
    that.
    David Harmon, Nov 28, 2006
    #6
  7. Grizlyk

    David Harmon Guest

    On Tue, 28 Nov 2006 07:05:17 GMT in comp.lang.c++, David Harmon
    <> wrote,
    >Base classes and members with constructors should have their own
    >constructors.


    Should be:

    Base classes and members with constructors should have their own
    destructors.
    David Harmon, Nov 29, 2006
    #7
  8. Grizlyk

    BobR Guest

    Grizlyk wrote in message ...
    >
    >It is absolutly unclear why "If a constructor throws an exception, the
    >object's destructor is not run."


    When an exception is thrown, it immediately leaves that point and starts
    searching for an handler (like a 'catch'). It won't finish what it was doing
    (in this case, constructing an object). If an object is not fully
    constructed, it has no destructor to call (loosely speaking. See David's
    post).

    > One can imagin too diffrent parts of
    >ctor of any class, they looks like
    >1. list of initializers


    [Function–level try blocks]

    class Boom : public Abase{
    Boom( int a ) try : Abase(a){}catch(...){/*something*/ throw; }
    };

    >2. code in {}


    class Boom{
    int Int;
    Boom( int a ){
    try{
    Int = a;
    }
    catch(...){/*something*/ throw; }
    }
    };

    >And there is absolutely no sence to treat exception from them equally.
    >
    >I think, the nesgroup is group of very busy men. It is better to me to
    >find other :)



    See: "Thinking In C++" Volume 2: Practical Programming
    (Bruce Eckel, Chuck Allison)

    Part 1: Building Stable Systems
    1: Exception handling

    Get "Thinking in C++", 2nd ed. Volume 1&2 by Bruce Eckel
    (available for free here. You can buy it in hardcopy too.):
    http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html

    --
    Bob R
    POVrookie
    BobR, Nov 29, 2006
    #8
  9. Grizlyk

    Grizlyk Guest

    Good morning.

    The two last posts: "David Harmon 28.11.2006 10:05 " and "BobR
    29.11.2006 05:17 " is really very interesting to me. I will consider
    them for nearest time.

    And if it is interesting to anybody, i can explain my doubts more:
    David Harmon wrote:
    "If the constructor throws, the object is not completely constructed."
    It is true.
    "The destructor has no way to know that."
    But it is not true. Programmer, of course, can not control a
    construction sequence, but the compiler can know, what the "imagined"
    part of constructor failed. Let's consider the next class:

    struct A
    {
    int* a; //need array of int[1024]
    int b;
    double c;

    A()throw(exceprion&); //must allocate memory with new[]
    ~A()throw(); //must free memory
    };

    This class has only members of base C++ types. They could not throw
    exception while creating own memory or data :). They have own
    predefined destructors (~int etc), which completele restore system to
    state befor the members created and compiler can know about their
    destructors and _can calls them if the members were created_.

    Lets write simple destructor of the class A:
    ~A::A()throw(){delete[] a; a=0;}
    The destructor of the class A has the one serious trouble - it can not
    calls "delete[]" of member "a" befor operator new will return a correct
    memory pointer. The data, stored in "a", is indefinite befor new will
    return.

    If compiler calls the dtor of the class A befor new will return a
    correct memory pointer, the program will crash and make at least
    "general protection fail".

    Selecting way of creation ctor of the class A, we can do like this
    A::A()throw(exceprion&):a(0){/*some code*/}
    The trick ":a(0)" makes dtor callings safe nearly independent from ctor
    code in {}.

    It is enough to guess, that compiler _can not_ call dtor of A, if list
    of initializers (ctor part one) failed, and _can_ call dtor of A if all
    members have been complete created successfully.

    There is another important argument to the upper described compiler
    behavior: all the class A members of predefined types has own ctor/dtor
    pairs, i.e. all the class A members are true classes with full
    incapsulated behaviour (they no need external control).

    Compiler can manage creating/destroying of the members by "calling"
    their trivial ctor/dtor. And executed dtor of any member of the class A
    completely restore the piece of system state, which has been changed by
    creating the member.

    And the ctor "code in {}" of the class A must be managed by compiler in
    the same manner. In order to restore all canges, have been created or
    partial (not complete) created by ctor "code in {}" execution, compiler
    _must_ call its dtor pair, else programmer will have to call dtor by
    hand.

    I do not see any sence to call destructor code from constructor myself,
    for it is can be easy done by compiler.

    To do it compiler can create ctor as two "imagined" part: "member_ctor"
    and "user_code_ctor". The "member_ctor" code is hidden to programmer,
    the correct ctor of any member of the class A is controlled by
    programmer with the help of list of initislizers. The goal of the
    "member_ctor" - make calls dtor of its class safe. The goal of
    "user_code_ctor" is control of its class memory. And dtor parted the
    same.

    Using the pattern we will have something like this:

    {
    A obj;
    //A::compiled_ctor
    // A::member_ctor(); if(exception)goto member_ex
    // A::user_code_ctor(); if(exception)goto user_ex

    ....

    //A::compiled_dtor
    //user_ex:
    // ~A::user_code_dtor();
    //member_ex:
    // ~A::member_dtor();
    }


    It is evidently, compiler must _not_ call "user_code_dtor()" if
    "user_code_ctor()" is even not started and must call "user_code_ctor()"
    if any part of "user_code_dtor()" executed in order to restore system
    state.

    In my first example any member of the class A has trivial ctor/dtor and
    can not throw any exception. Let's consider second example:

    struct B
    {
    A a;
    char* b; //need array of char[256]
    double c;

    B()throw(exceprion&); //must allocate memory with new[]
    ~B()throw(); //must free memory
    };

    The class A already can throw exception, but the pattern of its
    ctor/dtor creating allows to compiler to use class A as true class
    (with its own hidden ctor/dtor behaviour), calling
    "A::compiled_ctor"/"A::compiled_dtor" in
    "B::member_ctor()"/"B::member_dtor()". The pattern of ctor/dtor allows
    to compiler restore all changes maked by partial or complete created
    the member A of any class B.

    One can see: any class has either members of only predefined types with
    their trivial ctor/dtor and can use the ctor/dtor pattern (similar to
    the class A), or members like the class B (similar to the class B). For
    both cases the ctor/dtor pattern can be used, so destructor code can be
    always called by compiler.

    I can understand, that my explanaition is quite week and can not be
    trusted "in general case".

    In order to expand the explanaition to "general case" a man must be an
    expert of C++ real programming and know many practical cases, to find
    exceptions from my explanation. I do not see any exceptions :)

    Can you object me?
    Grizlyk, Nov 29, 2006
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jacques Labuschagne

    Explicit ctor/dtor calls

    Jacques Labuschagne, Jul 20, 2003, in forum: C++
    Replies:
    3
    Views:
    344
    Michael Kochetkov
    Jul 20, 2003
  2. Neal Becker

    #elements of seq A in seq B

    Neal Becker, Aug 20, 2009, in forum: Python
    Replies:
    2
    Views:
    253
    Raymond Hettinger
    Aug 21, 2009
  3. Jan Kaliszewski

    Re: #elements of seq A in seq B

    Jan Kaliszewski, Aug 20, 2009, in forum: Python
    Replies:
    4
    Views:
    281
    Jan Kaliszewski
    Aug 21, 2009
  4. , India
    Replies:
    2
    Views:
    311
    Stefan Ram
    Mar 30, 2010
  5. Alex Willmer
    Replies:
    14
    Views:
    313
    Steven D'Aprano
    Feb 29, 2012
Loading...

Share This Page