More effective C++

Discussion in 'C++' started by vikram_p_nayak@yahoo.com, Jun 15, 2006.

  1. Guest

    I was just going through this book by Scott Meyers and did not fully
    follow item 10.
    Basically he says the constructor should catch all exceptions, do any
    cleanups necessary and then throw. This is because otherwise the member
    objects already created can not be destroyed from outside since the
    container object itself is not fully created.

    Look at this - (myclass contains objects of class1 and class2)
    Class myclass {
    class1 m_class1;
    class2 m_class2;
    public:
    myclass(int c1, int c2):m_class1(c1), m_class2(c2) {}
    }

    Assume both class1 and class2 have constructors which take int
    parameters.
    so m_class1 gets initialized first and then m_class2. If the contructor
    of class2 throws an exception, even after its own cleanup, how would it
    help? m_class1 is already contructed. How is it destroyed?

    I actually tried this on VC++ 6 compiler and it seems to work. As in ,
    m_class1's destructor indeed gets called! I think class1 and class2
    initializations are independently happening and we can not cleanup one
    because of an exception in the other.

    Please let me know if I am missing something here. How does the
    compiled code know that m_class1 is contructed and it needs to be
    destroyed whereas it passes the exception from m_class2 as is.
     
    , Jun 15, 2006
    #1
    1. Advertising

  2. Pete Becker Guest

    wrote:
    >
    > Assume both class1 and class2 have constructors which take int
    > parameters.
    > so m_class1 gets initialized first and then m_class2. If the contructor
    > of class2 throws an exception, even after its own cleanup, how would it
    > help? m_class1 is already contructed. How is it destroyed?
    >


    The compiler generates code that destroys it. The cleanup you need to do
    in response to an exception in a constructor is for things that don't
    have appropriate destructors.

    --

    Pete Becker
    Roundhouse Consulting, Ltd.
     
    Pete Becker, Jun 15, 2006
    #2
    1. Advertising

  3. Guest

    > The cleanup you need to do
    > in response to an exception in a constructor is for things that don't
    > have appropriate destructors.


    Thanks for the response. But I will have to disagree a bit. Infact in
    the example he has given in the book the cleanup code has
    delete theImage;
    etc where theImage is an object with a welldefined dtor.

    --Vikram
     
    , Jun 15, 2006
    #3
  4. Guest

    > The cleanup you need to do
    > in response to an exception in a constructor is for things that don't
    > have appropriate destructors.


    Thanks for the response. But I will have to disagree a bit. Infact in
    the example he has given in the book the cleanup code has
    delete theImage;
    etc where theImage is an object with a welldefined dtor.

    --Vikram
     
    , Jun 15, 2006
    #4
  5. Gernot Bauer Guest

    schrieb:
    >> The cleanup you need to do
    >> in response to an exception in a constructor is for things that don't
    >> have appropriate destructors.

    >
    > Thanks for the response. But I will have to disagree a bit. Infact in
    > the example he has given in the book the cleanup code has
    > delete theImage;
    > etc where theImage is an object with a welldefined dtor.


    The example in the book dynamically allocates memory with "new". The
    code snippet you have posted doesn't. Note the difference.
    Therefore, you need to delete theImage explicitly (at this point, the
    dtor of theImage will be called). When using new, you have the
    responsibility clear the memory on your own (but you could use something
    as boost::shared_ptr and give the responsibility of deletion to this
    class).

    > --Vikram


    hth,
    Gernot
     
    Gernot Bauer, Jun 15, 2006
    #5
  6. In message <>,
    writes
    >> The cleanup you need to do
    >> in response to an exception in a constructor is for things that don't
    >> have appropriate destructors.

    >
    >Thanks for the response. But I will have to disagree a bit. Infact in
    >the example he has given in the book the cleanup code has
    >delete theImage;
    >etc where theImage is an object with a welldefined dtor.


    Nope. theImage is a *pointer* to such an object. Pointers don't have
    destructors, so _you_ have to arrange that the object it points to gets
    destroyed, by calling delete.


    --
    Richard Herring
     
    Richard Herring, Jun 15, 2006
    #6
  7. Guest

    > The example in the book dynamically allocates memory with "new". The
    > code snippet you have posted doesn't. Note the difference.
    > Therefore, you need to delete theImage explicitly (at this point, the
    > dtor of theImage will be called). When using new, you have the
    > responsibility clear the memory on your own (but you could use something
    > as boost::shared_ptr and give the responsibility of deletion to this
    > class).


    Thanks Gernot. Yes, you are right about the example in the book. I was
    trying to demonstrate my confusion using objects rather than pointers
    because the book specifically says pointers better have a try/catch
    blocks for initializations, either in constructor or in a private
    member function. Please have a look at the initImage/initAudioImage
    methods on page 73 (atleast for the version of the book I have).

    In short, I am fine with all the examples that the book has. But I had
    a question regarding the whole concept when we have member
    initialization lists. How does the code know that m_class1 is already
    constructed and should be destroyed? The destructor of the container
    object does not get called since it has thrown an exception. So how it
    figure out the member objects that are fully created?

    Thanks,
    Vikram
     
    , Jun 15, 2006
    #7
  8. Howard Guest

    <> wrote in message
    news:...
    >> The example in the book dynamically allocates memory with "new". The
    >> code snippet you have posted doesn't. Note the difference.
    >> Therefore, you need to delete theImage explicitly (at this point, the
    >> dtor of theImage will be called). When using new, you have the
    >> responsibility clear the memory on your own (but you could use something
    >> as boost::shared_ptr and give the responsibility of deletion to this
    >> class).

    >
    > Thanks Gernot. Yes, you are right about the example in the book. I was
    > trying to demonstrate my confusion using objects rather than pointers
    > because the book specifically says pointers better have a try/catch
    > blocks for initializations, either in constructor or in a private
    > member function. Please have a look at the initImage/initAudioImage
    > methods on page 73 (atleast for the version of the book I have).
    >
    > In short, I am fine with all the examples that the book has. But I had
    > a question regarding the whole concept when we have member
    > initialization lists. How does the code know that m_class1 is already
    > constructed and should be destroyed? The destructor of the container
    > object does not get called since it has thrown an exception. So how it
    > figure out the member objects that are fully created?
    >


    Hi Vikram,

    an initialization list is not the same as code in the constructor body.
    Those member object initializers you show [:m_class1(c1), m_class2(c2)] are
    not normal function calls. They're more like instructions to the compiler
    as to _how_ the member objects should be initialized at construction time.
    So the normal rules still apply for construction and destruction (and thus
    the automatic calling of contructors and destructors).

    During construction, the members are constructed for you, in the order
    they are declared in the class (which need not be the same as the order of
    your initialization list, by the way). When the class is destroyed, the
    members are deconstructed in reverse order, and then the class object itself
    is destroyed. So all destructors get called automatically.

    It's only when you do dynamic allocations in your constructor (e.g.,
    using new) that you need to worry about making sure you call delete in the
    event of a failure in the constructor.

    -Howard
     
    Howard, Jun 15, 2006
    #8
  9. Howard Guest

    For me, the most annoying aspects of C++ are:

    - It's hard to spell.
    - It's not Delphi (or Java, or VB).
    - There's always that terrible nagging fear that, should I invoke Undefined
    Behavior (TM), my hard drive might catch fire, my email system send nasty
    messages to my boss, or I could accidently launch a Titan III missile at
    China, starting World War 3.

    Oh yeah, there's that annoying part about getting paid too much, too...

    -Howard
     
    Howard, Jun 15, 2006
    #9
  10. Howard Guest

    "Howard" <> wrote in message news:6xfkg.28520

    oops, wrong thread... <Cancel!><Cancel!!><Cancel!!!>...arrgh!

    (Now ask me about the most annoying things about Outlook Express...)
     
    Howard, Jun 15, 2006
    #10
  11. Howard wrote:
    > "Howard" <> wrote in message news:6xfkg.28520
    >
    > oops, wrong thread... <Cancel!><Cancel!!><Cancel!!!>...arrgh!
    >
    > (Now ask me about the most annoying things about Outlook Express...)


    LOL... THE Most annoying thing about comp.lang.c++:
    - All that whining about what's the most annoying thing in C++.
    The second most annoying thing about comp.lang.c++:
    - All those "questions" whether the asker should learn C++ or Java.

    V
    --
    Please remove capital 'A's when replying by e-mail
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Jun 15, 2006
    #11
  12. Guest

    > an initialization list is not the same as code in the constructor body.
    > Those member object initializers you show [:m_class1(c1), m_class2(c2)] are
    > not normal function calls. They're more like instructions to the compiler
    > as to _how_ the member objects should be initialized at construction time.
    > So the normal rules still apply for construction and destruction (and thus
    > the automatic calling of contructors and destructors).
    >
    > During construction, the members are constructed for you, in the order
    > they are declared in the class (which need not be the same as the order of
    > your initialization list, by the way). When the class is destroyed, the
    > members are deconstructed in reverse order, and then the class object itself
    > is destroyed. So all destructors get called automatically.
    >
    > It's only when you do dynamic allocations in your constructor (e.g.,
    > using new) that you need to worry about making sure you call delete in the
    > event of a failure in the constructor.
    >


    Hi Howard, I understand all of the above except for the line
    "When the class is destroyed, the members are deconstructed in reverse
    order, and then the class object itself is destroyed. So all
    destructors get called automatically."
    The problem here is the main container class (myClass in my example) is
    never destroyed. If the main destructor was being called, I fully
    understand how the contained objects' destructors would be called.

    Anyway, looks like I am making it more complicated than what it is. Let
    me think for a while and if I still dont follow, I will get back with a
    better example and specific questions.

    Thanks to all who replied.
    --Vikram
     
    , Jun 16, 2006
    #12
  13. > Hi Howard, I understand all of the above except for the line
    > "When the class is destroyed, the members are deconstructed in reverse
    > order, and then the class object itself is destroyed. So all
    > destructors get called automatically."
    > The problem here is the main container class (myClass in my example) is
    > never destroyed. If the main destructor was being called, I fully
    > understand how the contained objects' destructors would be called.
    >
    > Anyway, looks like I am making it more complicated than what it is. Let
    > me think for a while and if I still dont follow, I will get back with a
    > better example and specific questions.
    >
    > Thanks to all who replied.
    > --Vikram


    The reason the "main container class" is never destroyed is because
    it's never constructed (fully). If the destructor was called you
    wouldn't know which part of the object was constructed (and should be
    destroyed) and which parts was not constructed and therefore shouldn't
    be destroyed. The only place where you (or the compiler) knows which
    parts of the object has been constructed is in the constructor.

    There is actually two cases here:

    1. An exception is thrown in the construction of any of the possible
    base objects (class A : public Base { /* ... */ }; ) or any of the
    member objects (either specified in the initialization list or just
    default constructed if not part of the list). In this case the compiler
    knows what to do and calls the destructor of all objects contructed so
    far in reverse order. And the contructor body of your class i never
    called. This means any contruction that should have taken place there
    is not performed and therefore you don't need to call the destructor.

    2. If all base and member objects are contructed ok, the contructor
    body of your class is reached. In this case you have to be careful
    since if you make any resource allocations (like memory with new) and
    something makes your contructor throw later in the construcotr, your
    destructor is *not* called since the object is not fully contructed.
    You therefore have to guard the contructor code with try/catch (or use
    RAII objects like smart pointers - auto_ptr or boost::shared_ptr which
    are automaticall destroyed) and destroy any objects allocated so far
    manually with delete or similar.

    / Påhl
     
    =?iso-8859-1?B?UOVobCBNZWxpbg==?=, Jun 16, 2006
    #13
  14. Gernot Bauer Guest

    On 2006-06-15, <> wrote:
    >> The example in the book dynamically allocates memory with "new". The
    >> code snippet you have posted doesn't. Note the difference.
    >> Therefore, you need to delete theImage explicitly (at this point, the
    >> dtor of theImage will be called). When using new, you have the
    >> responsibility clear the memory on your own (but you could use something
    >> as boost::shared_ptr and give the responsibility of deletion to this
    >> class).


    [snip]

    > In short, I am fine with all the examples that the book has. But I had
    > a question regarding the whole concept when we have member
    > initialization lists. How does the code know that m_class1 is already
    > constructed and should be destroyed? The destructor of the container
    > object does not get called since it has thrown an exception. So how it
    > figure out the member objects that are fully created?


    The compiler has the responsibility to figure this out and to destroy the
    objects that it has created for you. It allocates memory for your members and
    constructs them either by using a constructor without parameters or a
    specialized one if you have stated one or more arguments in the initialization
    list. When the object's lifetime is over, it destroys these member objects.

    When an object cannot be build due to an exception, the desctructor will not
    be called, as you mention correctly. This is due to the fact that the object
    never was really "alive". In this case, the compiler will destruct members
    that were successfully created by the compiler when the constructor is
    left due to an exception.

    > Thanks,
    > Vikram


    Regards,
    Gerno
     
    Gernot Bauer, Jun 17, 2006
    #14
  15. On 2006-06-15, <> wrote:
    >> The example in the book dynamically allocates memory with "new". The
    >> code snippet you have posted doesn't. Note the difference.
    >> Therefore, you need to delete theImage explicitly (at this point, the
    >> dtor of theImage will be called). When using new, you have the
    >> responsibility clear the memory on your own (but you could use something
    >> as boost::shared_ptr and give the responsibility of deletion to this
    >> class).


    [snip]

    > In short, I am fine with all the examples that the book has. But I had
    > a question regarding the whole concept when we have member
    > initialization lists. How does the code know that m_class1 is already
    > constructed and should be destroyed? The destructor of the container
    > object does not get called since it has thrown an exception. So how it
    > figure out the member objects that are fully created?


    The compiler has the responsibility to figure this out and to destroy the
    objects that it has created for you. It allocates memory for your members and
    constructs them either by using a constructor without parameters or a
    specialized one if you have stated one or more arguments in the initialization
    list. When the object's lifetime is over, it destroys these member objects.

    When an object cannot be build due to an exception, the desctructor will not
    be called, as you mention correctly. This is due to the fact that the object
    never was really "alive". In this case, the compiler will destruct members
    that were successfully created by the compiler when the constructor is
    left due to an exception.

    > Thanks,
    > Vikram


    Regards,
    Gerno
     
    Gernot Robert Bauer, Jun 17, 2006
    #15
  16. On 2006-06-15, <> wrote:
    >> The example in the book dynamically allocates memory with "new". The
    >> code snippet you have posted doesn't. Note the difference.
    >> Therefore, you need to delete theImage explicitly (at this point, the
    >> dtor of theImage will be called). When using new, you have the
    >> responsibility clear the memory on your own (but you could use something
    >> as boost::shared_ptr and give the responsibility of deletion to this
    >> class).


    [snip]

    > In short, I am fine with all the examples that the book has. But I had
    > a question regarding the whole concept when we have member
    > initialization lists. How does the code know that m_class1 is already
    > constructed and should be destroyed? The destructor of the container
    > object does not get called since it has thrown an exception. So how it
    > figure out the member objects that are fully created?


    The compiler has the responsibility to figure this out and to destroy the
    objects that it has created for you. It allocates memory for your members and
    constructs them either by using a constructor without parameters or a
    specialized one if you have stated one or more arguments in the initialization
    list. When the object's lifetime is over, it destroys these member objects.

    When an object cannot be build due to an exception, the desctructor will not
    be called, as you mention correctly. This is due to the fact that the object
    never was really "alive". In this case, the compiler will destruct members
    that were successfully created by the compiler when the constructor is
    left due to an exception.

    > Thanks,
    > Vikram


    Regards,
    Gernot
     
    Gernot Robert Bauer, Jun 17, 2006
    #16
    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. Replies:
    4
    Views:
    450
  2. Josh Mcfarlane
    Replies:
    44
    Views:
    1,156
    Alf P. Steinbach
    Dec 16, 2005
  3. barcaroller

    More Effective C++

    barcaroller, Apr 13, 2007, in forum: C++
    Replies:
    4
    Views:
    459
    Default User
    Apr 13, 2007
  4. Replies:
    3
    Views:
    328
  5. Boni Satani
    Replies:
    0
    Views:
    194
    Boni Satani
    Jan 9, 2014
Loading...

Share This Page