Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Constructor Try Block

6 views
Skip to first unread message

jsh...@bigfoot.com

unread,
Jan 16, 1999, 3:00:00 AM1/16/99
to
Hi,
Can anyone give me a meaningful use of a constructor try block.

For eg.
class cont
{
A a_;
public:
cont(A & a)
try
:a_(a)
{ /* cont constructor code */ }
catch(//whatever) { /* handler code */ }
};

Normally, one catches an exception in a constructor,
to maybe do some cleanup or free acquired resources,
before maybe rethrowing the exception.

But this can be done even when you have
a try block inside the constructor,
(i.e cont(A &a){ try{}catch(){}} )
Here,you would catch all the exceptions, except the ones thrown by
the member objects constructors, which I think would suffice,
because, anyway what can you do, in the handler, in case of a
subobject constructor failing ? The destructor would be called for any
other subobject which already been created anyway. And you haven't
started
doing anything in your constructor, so you do not have any clean up to
do. I
think it's best to let this exception(the one thrown by the subobject)
propagate the code which created a "cont" object & let it handle it.

Or can anyone else can give an example where a constructor try block
would be useful ?

cheers,
Shiva
comp.lang.c++ FAQ :http://www.cerfnet.com/~mpcline/c++-faq-lite/
http://members.xoom.com

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own


[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

Siemel Naran

unread,
Jan 17, 1999, 3:00:00 AM1/17/99
to
On 16 Jan 1999 22:16:02 -0500, jsh...@bigfoot.com <jsh...@bigfoot.com> wrote:

>(i.e cont(A &a){ try{}catch(){}} )
>Here,you would catch all the exceptions, except the ones thrown by
>the member objects constructors, which I think would suffice,
>because, anyway what can you do, in the handler, in case of a
>subobject constructor failing ? The destructor would be called for any
>other subobject which already been created anyway. And you haven't
>started
>doing anything in your constructor, so you do not have any clean up to
>do. I
>think it's best to let this exception(the one thrown by the subobject)
>propagate the code which created a "cont" object & let it handle it.

If the subobject constructor -- ie, an initialization in the member
list -- fails, then you might want to catch the exception, do some
error reporting, then rethrow the exception. The 'catch' block
outside the constructor body lets you do this. What does the
standard say about this particular 'catch' block? Shouldn't it
have to rethrow, or throw a new exception?

--
----------------------------------
Siemel B. Naran (sbn...@uiuc.edu)
----------------------------------

Siemel Naran

unread,
Jan 18, 1999, 3:00:00 AM1/18/99
to
On 16 Jan 1999 22:16:02 -0500, jsh...@bigfoot.com <jsh...@bigfoot.com> wrote:

> Can anyone give me a meaningful use of a constructor try block.

Here's a more meaningful use. Suppose you write a class whose
constructor takes a pointer to a dynamically allocated object.
The destructor of the class deletes the pointed-to-object.
Something like this:

class X {
private:
std::auto_ptr<int> i1;
std::auto_ptr<int> i2;
public:
X(int * i1_, int * i2_) : i1(i1_), i2(i2_) { }
X(const X&);
X& operator=(const X&);
}

What if the constructor of 'i1' throws an exception? Then the
objects pointed to by 'i1_' and 'i2_' (note the underscore) are
not deleted. What if the constructor of 'i2' throws an
exception? Then 'i1' is destroyed and the object pointed to
by 'i1_' is deleted. But the object pointed to by 'i2_' is
not deleted.

So the solution is to catch the exceptions that std::auto_ptr
may throw in a catch block outside the constructor, delete the
pointed to objects, and then rethrow. The above example
presents a problem because how de we distinguish exceptions
arising from initialization of 'i1' and exceptions arising
from initialization of 'i2'? If the exception comes from 'i1',
then delete should be applied to 'i1_' and 'i2_'. If the
exception comes from 'i2', then delete should be applied to
'i2_'.

I have no idea whether my reasoning is correct or not. My
compiler 'como' does not handle try-catch block outside
constructor and destructor bodies. My compiler 'egcs'
compiles the code, but does not do what I expect.

Here's a program.

--------------------------------------------------------------------

#include <iostream.h>
//#include <memory> // auto_ptr
#include "/usr/include/STL/memory"
#include <string>

class Thing
{
private:
const std::string t;
public:
struct Error { static std::string tag() { return "throw"; } };
explicit Thing(std::string t_) : t(t_)
{
if (t==Error::tag()) throw Error();
cout << "Thing ctor : "<<t<<"\n";
}
Thing(const Thing& T) : t(T.t) { cout << "Thing copy ctor : "<<t<<"\n"; }
~Thing() { cout << "Thing dtor : "<<t<<"\n"; }
std::string val() const { return t; }
};

class Class
{
private:
Thing t1;
std::auto_ptr<Thing> t2;
public:
Class(std::string t1, Thing * t2);
Class(const Class&);
Class& operator=(const Class&);
};

// non exception-safe version
//Class::Class(std::string t1_, Thing * t2_) : t1(t1_), t2(t2_) { }

// supposedly exception-safe version
Class::Class(std::string t1_, Thing * t2_) try : t1(t1_), t2(t2_) { }
catch(Thing::Error) { delete t2_; throw; }

int main()
{
try
{
cout << "start try\n";
//Class c("t1",new Thing("t2"));
Class c(Thing::Error::tag(),new Thing("t2"));
cout << "end try\n";
}

catch(Thing::Error) { cout << "caught Thing::Error\n"; }
}
--------------------------------------------------------------------

The expected output is
start try
Thing ctor : t2
Thing dtor : t2
caught Thing::Error

After the words "start try", we attempt to create an object
'c' of type Class.
Class c(Thing::Error::tag(),new Thing("t2"));
The first argument returns the string "throw". The second
argument returns a pointer to a newly created Thing object,
resulting in the words "Thing ctor : t2". No problem so far.

The creation of c.t1 invokes Thing::Thing(std::string) with
string=="throw". This causes the constructor to throw an
exception, which means that c1 was not fully created, which in
turn means that c1's destructor should not be called. Then we
enter the catch block outside Class's constructor. At this
point, the object pointed to by "t2_" is deleted. So we see
the words "Thing dtor : t2".

Then the catch handler in main(...) catches the exception,
and we see the words "caught Thing::Error".


Instead, the output I get is
start try
Thing ctor : t2
Thing dtor : throw
caught Thing::Error
The third line is different from my expected output. It appears
that the unconstructed object 't1' is being deleted, and the
delete statement in the catch block does not execute.

So what goes?

jsh...@bigfoot.com

unread,
Jan 18, 1999, 3:00:00 AM1/18/99
to
In article <slrn7a30c1....@localhost.localdomain>,

sbn...@uiuc.edu wrote:
> On 16 Jan 1999 22:16:02 -0500, jsh...@bigfoot.com <jsh...@bigfoot.com> wrote:
>
> >(i.e cont(A &a){ try{}catch(){}} )
> >Here,you would catch all the exceptions, except the ones thrown by
> >the member objects constructors, which I think would suffice,
> >because, anyway what can you do, in the handler, in case of a
> >subobject constructor failing ? The destructor would be called for any
> >other subobject which already been created anyway. And you haven't
> >started
> >doing anything in your constructor, so you do not have any clean up to
> >do. I
> >think it's best to let this exception(the one thrown by the subobject)
> >propagate the code which created a "cont" object & let it handle it.
>
> If the subobject constructor -- ie, an initialization in the member
> list -- fails, then you might want to catch the exception, do some
> error reporting, then rethrow the exception. The 'catch' block
> outside the constructor body lets you do this. What does the
> standard say about this particular 'catch' block? Shouldn't it
> have to rethrow, or throw a new exception?
>

The standard says that if the control reaches the end of
the handler of a function try block of a c'tor/d'tor, its automatically
rethrown. ie. if you do not throw an exception, it will automatically
rethrow the exception.

So, you can only do error reporting in a ctor try block & not any meaningful
error handling.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Vaclav Barta

unread,
Jan 18, 1999, 3:00:00 AM1/18/99
to
jsh...@bigfoot.com wrote:
> Can anyone give me a meaningful use of a constructor try block.
[example snipped]

> But this can be done even when you have
> a try block inside the constructor,
> (i.e cont(A &a){ try{}catch(){}} )
> Here,you would catch all the exceptions, except the ones thrown by
> the member objects constructors, which I think would suffice,
> because, anyway what can you do, in the handler, in case of a
> subobject constructor failing ? The destructor would be called for any
> other subobject which already been created anyway. And you haven't
> started
> doing anything in your constructor, so you do not have any clean up to
> do. I
> think it's best to let this exception(the one thrown by the subobject)
> propagate the code which created a "cont" object & let it handle it.
>
> Or can anyone else can give an example where a constructor try block
> would be useful ?
I've never used a constructor try block, but one reason for it
could be to add information from cont to the exception. For example
catch
clause for exceptions thrown by cont may want to know the identity of
failed cont - let's say all cont objects have a name, known to the
object
and its creator, but not to members of cont. Then it would be useful to
write

class cont
{
string name_;
A a_; // ctor may throw failure_desc
public:
cont(const string &name)
try
:name_(name) { }
catch(const failure_desc &d) { throw cont_failure(name, d); }
};

Please note I never tried that and IMHO it's too much of a bleeding
edge to use in real programs - but it looks impressive... :-)

Bye
Vasek
--
I have a search engine, too!
http://www.locus.cz/locus/

Siemel Naran

unread,
Jan 18, 1999, 3:00:00 AM1/18/99
to
On 18 Jan 1999 00:43:39 -0500, jsh...@bigfoot.com <jsh...@bigfoot.com>
wrote:

>So, you can only do error reporting in a ctor try block & not any
meaningful
>error handling.

Well, you can do error reporting. But you can also throw a new
exception -- ie, use the catch block as an exception adaptor
that converts one exception to another. Or you can access the
variables in the constructor argument list, and possibly
delete dynamically allocated objects, as in my other post.

But this technique is new to me, so I might be off the mark.

--
----------------------------------
Siemel B. Naran (sbn...@uiuc.edu)
----------------------------------

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Christopher Eltschka

unread,
Jan 18, 1999, 3:00:00 AM1/18/99
to
Siemel Naran wrote:

>
> On 16 Jan 1999 22:16:02 -0500, jsh...@bigfoot.com <jsh...@bigfoot.com> wrote:
>
> > Can anyone give me a meaningful use of a constructor try block.
>

An interesting question (although I doubt that auto_ptr constructor
may throw; but a1 and a2 might be other classes with pointer sink
behaviour). Maybe it could be solved like this:

X(int* i1_, int* i2_) try :
i1(i1_), // as before
i2(i1_=NULL, i2_) // before calling i2(int*), set i1_ to 0
{
}
catch(...)
{
delete i2_; // safe in any case
delete i1_; // safe: if i2 has thrown, i1_ was set to NULL
}

[...]

Dave Abrahams

unread,
Jan 19, 1999, 3:00:00 AM1/19/99
to

Siemel Naran wrote in message ...
>
>Here's a more meaningful use.....

> class X {
> private:
> std::auto_ptr<int> i1;
> std::auto_ptr<int> i2;
> public:
> X(int * i1_, int * i2_) : i1(i1_), i2(i2_) { }
> X(const X&);
> X& operator=(const X&);
> }
>
>What if the constructor of 'i1' throws an exception?

That's silly; the constructor of std::auto_ptr<int> can't throw an
exception.

<snip>


>So the solution is to catch the exceptions that std::auto_ptr

>may throw in a catch block outside the constructor, delete the...
<snip>

That's a lot of trouble to go to to prevent the impossible from happening.

Francis Glassborow

unread,
Jan 19, 1999, 3:00:00 AM1/19/99
to
Am I being stupid again? :)

In article <36A3814D...@physik.tu-muenchen.de>, Christopher
Eltschka <celt...@physik.tu-muenchen.de> writes


>An interesting question (although I doubt that auto_ptr constructor
>may throw; but a1 and a2 might be other classes with pointer sink
>behaviour). Maybe it could be solved like this:
>
> X(int* i1_, int* i2_) try :
> i1(i1_), // as before
> i2(i1_=NULL, i2_) // before calling i2(int*), set i1_ to 0

Why are you setting a pointer to an external int to NULL?

> {
> }
> catch(...)
> {
> delete i2_; // safe in any case

Why are you deleting an external object via a pointer passed as an
argument to a ctor? This seems doomed to catastrophe

> delete i1_; // safe: if i2 has thrown, i1_ was set to NULL

So who is responsible for deleting the object originally pointed to by
i1_ ? There does not seem to be any logic to the actions being taken.


Francis Glassborow Chair of Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

Siemel Naran

unread,
Jan 19, 1999, 3:00:00 AM1/19/99
to
On 18 Jan 1999 23:01:55 -0500, Christopher Eltschka
>Siemel Naran wrote:

>> Here's a more meaningful use. Suppose you write a class whose
>> constructor takes a pointer to a dynamically allocated object.
>> The destructor of the class deletes the pointed-to-object.
>> Something like this:
>>

>> class X {
>> private:
>> std::auto_ptr<int> i1;
>> std::auto_ptr<int> i2;
>> public:
>> X(int * i1_, int * i2_) : i1(i1_), i2(i2_) { }

>An interesting question (although I doubt that auto_ptr constructor
>may throw; but a1 and a2 might be other classes with pointer sink
>behaviour). Maybe it could be solved like this:
>
> X(int* i1_, int* i2_) try :
> i1(i1_), // as before
> i2(i1_=NULL, i2_) // before calling i2(int*), set i1_ to 0

> {
> }
> catch(...)
> {
> delete i2_; // safe in any case

> delete i1_; // safe: if i2 has thrown, i1_ was set to NULL
> }

OK. This solves the problem for all pointers.

But in general, it might be nice to know which member threw the
exception because in general there might be special steps to take
depending on which member threw the exception. My proposed
solution would be to have multiple try-catch blocks outside the
initialization list. Maybe this is overkill, as I doubt the
feature would be much used anyway.

[David Abrahams]


>That's silly; the constructor of std::auto_ptr<int> can't throw an
>exception.

Right. But in general, the member initializer of the first object
may throw. My post went on to give a more realistic example of a
class Class whose first member is Class::t1 and has type "Thing"
and whose second member is Class::t2 that has type "auto_ptr<Thing>".
The constructor of Class is
Class::Class(int t1_, Thing * t2_) : t1(t1_), t2(t2_) { }
The object 't1' is constructed first, and Thing's constructor may
throw an exception. Then someone has to delete 't2_'. Code like
this does occur in practice. This example was slightly harder
to read, but it was compilable, at least on egcs.

The reason for mu auto_ptr example was twofold. First, the class
Class problem could be solved by making the auto_ptr member come
first,
class Class { std::auto_ptr<Thing> t1; Thing t2; ... };
But I wanted people to think of the general use of try-catch blocks
outside constructors rather than use the above easy solution (but
production code should usually use the easiest solution, as it
leads to code that is easiest to maintain). Second, I wanted to
present the general problem of how do you deal with exceptions in
catch blocks because you don't know which member initializer threw
the exception. Christopher's solution of setting the pointer to
NULL solves the problem for the special case of pointers.

[Francis Glassborrow]


>Why are you setting a pointer to an external int to NULL?

>Why are you deleting an external object via a pointer passed as an
>argument to a ctor? This seems doomed to catastrophe

>So who is responsible for deleting the object originally pointed to by
>i1_ ? There does not seem to be any logic to the actions being taken.

Note that clients use class Class like this:

int main()
{
Class c(3,new Thing(3));
}


We observe that class Thing is a base class for other objects. It
is usually abstract (meaning that it has at least one pure virtual
function, but usually all functions are pure virtual, except for
the destructor). The destructor is virtual so that when Class's
destructor deletes the Thing object the correct destructor
DerivedThing::~DerivedThing() is called.

In real code, class Thing may be a creator or factory. Instead of
"Thing", it would be called "Factory" or "Reader". And instead of
"Class", we would use "Parser" or "Catalog". The member functions
of class Parser, defined in files 'Parser.h' and 'Parser.c' would
work exclusively with the virtual functions of class Factory. But
clients use concrete factories and concrete readers. So client
code looks like this:

#include "Parser.h"
#include "DerivedFactory3.h"
int main()
{
Parser parser(new DerivedFactory3("myfile.h"),Color::RED);
}

--
----------------------------------
Siemel B. Naran (sbn...@uiuc.edu)
----------------------------------

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Christopher Eltschka

unread,
Jan 20, 1999, 3:00:00 AM1/20/99
to
Francis Glassborow wrote:
>
> Am I being stupid again? :)
>
> In article <36A3814D...@physik.tu-muenchen.de>, Christopher
> Eltschka <celt...@physik.tu-muenchen.de> writes
> >An interesting question (although I doubt that auto_ptr constructor
> >may throw; but a1 and a2 might be other classes with pointer sink
> >behaviour). Maybe it could be solved like this:
> >
> > X(int* i1_, int* i2_) try :
> > i1(i1_), // as before
> > i2(i1_=NULL, i2_) // before calling i2(int*), set i1_ to 0
> Why are you setting a pointer to an external int to NULL?
>
> > {
> > }
> > catch(...)
> > {
> > delete i2_; // safe in any case
> Why are you deleting an external object via a pointer passed as an
> argument to a ctor? This seems doomed to catastrophe
>
> > delete i1_; // safe: if i2 has thrown, i1_ was set to NULL
>
> So who is responsible for deleting the object originally pointed to by
> i1_ ? There does not seem to be any logic to the actions being taken.

If you had looked at the post I was responding to (and my remark
above the code you quoted), it should have been obvious.

Note that I'm not arguing about if this is good or bad design,
but just gave a possible solution to the question raised in
the posting I responded to (with a slight change noted in
my remark, due to the fact that auto_ptr constructor doesn't
throw).
Given the desired semantics (the ownership it taken by X, and
X destroys the pointers, even if the construction fails), and
the given interface (two raw pointers), this is the simplest
solution.
With an interface change, the same semantics could have
implemented much cleaner:

X(auto_ptr<int> i1_, auto_ptr<int> i2_):
i1(i1_), // or i1_(i1.release()), if there's only an int* constructor
i2(i2_) // same here, of course
{
}

Do you agree that _this_ code has clean semantics? Now,
the semantics of the above code is the same - just with an
interface which makes it harder to implement (and harder to
see by the user of the class - only documentation can tell
him).

Gargantua Blargg

unread,
Jan 21, 1999, 3:00:00 AM1/21/99
to
In article <slrn7a6tkd....@localhost.localdomain>,
sbn...@uiuc.edu wrote:

> On 18 Jan 1999 00:43:39 -0500, jsh...@bigfoot.com <jsh...@bigfoot.com>
> wrote:
>
> >So, you can only do error reporting in a ctor try block & not any
> meaningful
> >error handling.
>
> Well, you can do error reporting. But you can also throw a new
> exception -- ie, use the catch block as an exception adaptor
> that converts one exception to another.

Yes. This is one of the main purposes for construct try blocks that I see.
Without it, it would be impossible to convert exceptions thrown by members
and bases to some other type of exception.

> Or you can access the
> variables in the constructor argument list, and possibly
> delete dynamically allocated objects, as in my other post.

(I don't want to respond to that mess of like-named local parameters as
member variables, with constructor catch blocks, in the other branch of
this thread :-)

Like the resource-is-acquisition technique, I suggest that you break this
handling into objects that are the arguments themselves. Having a ctor
that takes auto_ptr<>s is an example of this.

Simply put, catch blocks that catch all exceptions, do some cleanup, then
rethrow the exception, are error-prone, because, at the very least, they
likely duplicate cleanup code from elsewhere.

Catch blocks are for handling of specific exceptions types.

Destructors are for cleanup.

--
"I don't like my edges rounded off" - Ani DiFranco

Gargantua Blargg | bla...@flash.net | http://www.flash.net/~blargg/

0 new messages