ctor/dtor calls and ctor init seq

G

Grizlyk

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

Alf P. Steinbach

* 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.
 
G

Grizlyk

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 _ÓÔÁÎÄÁÒÔ_.
 
D

David Harmon

On 25 Nov 2006 20:06:39 -0800 in comp.lang.c++, "Grizlyk"
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/
 
G

Grizlyk

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 :)
 
D

David Harmon

On 27 Nov 2006 20:16:20 -0800 in comp.lang.c++, "Grizlyk"
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.
 
D

David Harmon

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

Should be:

Base classes and members with constructors should have their own
destructors.
 
B

BobR

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
 
G

Grizlyk

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?
 

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,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top