More effective C++

V

vikram_p_nayak

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.
 
P

Pete Becker

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.
 
V

vikram_p_nayak

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
 
V

vikram_p_nayak

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
 
G

Gernot Bauer

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).

hth,
Gernot
 
R

Richard Herring

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.
 
V

vikram_p_nayak

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
 
H

Howard

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
 
H

Howard

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
 
H

Howard

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

(Now ask me about the most annoying things about Outlook Express...)
 
V

Victor Bazarov

Howard said:
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
 
V

vikram_p_nayak

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
 
?

=?iso-8859-1?B?UOVobCBNZWxpbg==?=

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
 
G

Gernot Bauer

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
 
G

Gernot Robert Bauer

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
 
G

Gernot Robert Bauer

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top