why are thrown objects copied twice?

F

fabio de francesco

Hi,

I would like to know why objects that are thrown to be caught are
copy-constructed more than once.

I have seen this behaviour after running the following simple program
that is an exercise (adapted from F.Brokken C++ Annotations) only to
see constructors at work:

/* exception.cpp */

#include <iostream>
#include <string>

using namespace std;

class Object
{
string name;
public:
Object(string n)
:name(n)
{
cout << "Object constructor of " << name << "\n";
}
Object(Object const &other)
:name(other.name + " (copy)")
{
cout << "Copy constructor for " << name << "\n";
}
~Object()
{
cout << "Object destructor of " << name << "\n";
}
friend void fun_1(Object obj)
{}
void fun_2()
{
Object Thrown("local object");
throw Thrown;
}
char const *get()
{
return name.c_str();
}
};

int main()
{
Object out("main object");

fun_1(out);

try
{
out.fun_2();
}
catch(Object obj)
{
cout << "Caught exception from " << obj.get() << "\n";
}
}

This is the output:

[myself@localhost /temporary]# ./exception
Object constructor of main object
Copy constructor for main object (copy)
Object destructor of main object (copy)
Object constructor of local object
Copy constructor for local object (copy)
Object destructor of local object
Copy constructor for local object (copy) (copy)
Caught exception from local object (copy) (copy)
Object destructor of local object (copy) (copy)
Object destructor of local object (copy)
Object destructor of main object

As you can see the copy constructor adds "(copy)" to the name of the
newly created object. I also put the call to fun_1() just to persuade
myself that an object that is passed by value is copied only once.

May be I am missing something obvious.

Thank you all in advance,

Fabio De Francesco.
 
G

Gregg

(e-mail address removed) (fabio de francesco) wrote in
Hi,

I would like to know why objects that are thrown to be caught are
copy-constructed more than once.

You are catching by value instead of by reference. This will result in a
copy being made of the thrown object, just as it would if you passed it
to a function by value.

Instead of

catch (Object obj)

try

catch (const Object& obj)

The first copy is made when you are throwing the non-temporary local
object.

Gregg
 
N

Niels Dekker - no reply address

Fabio said:
I would like to know why objects that are thrown to be caught are
copy-constructed more than once.

Gregg replied:
You are catching by value instead of by reference. This will result in a
copy being made of the thrown object, just as it would if you passed it
to a function by value.

Instead of

catch (Object obj)

try

catch (const Object& obj)

Doesn't work in Fabio's case, because he wants to call a non-const
member function of "obj" inside the catch clause. So instead, what
about using a non-const reference? As follows:

catch (Object& obj)


Regards,

Niels Dekker
www.xs4all.nl/~nd/dekkerware
 
J

John Harrison

Niels Dekker - no reply address said:
Gregg replied:

Doesn't work in Fabio's case, because he wants to call a non-const
member function of "obj" inside the catch clause. So instead, what
about using a non-const reference? As follows:

catch (Object& obj)

It's OK, but better would be to declare get as const, like it should've been
in the first place.

char const *get() const
{
return name.c_str();
}

john
 
N

Niels Dekker - no reply address

Gregg said:
You are catching by value instead of by reference. This will result in a
copy being made of the thrown object, just as it would if you passed it
to a function by value.

Instead of
catch (Object obj)
try
catch (const Object& obj)
Doesn't work in Fabio's case, because he wants to call a non-const
member function of "obj" inside the catch clause. So instead, what
about using a non-const reference? As follows:

catch (Object& obj)

John Harrison replied:
It's OK, but better would be to declare get as const, like it should've been
in the first place.

char const *get() const
{
return name.c_str();
}


You're right, Object::get() should definitely be a const member
function. But still I find catching by non-const reference kinda
interesting... being allowed to modify that temporary exception object!
It still appears to catch const objects as well:

try
{
const Object ConstObj("const object");
throw ConstObj;
}
catch (Object& obj) // non-const reference
{
cout << "Caught exception from " << obj.get() << "\n";
}

Of course, it only catches a non-const copy of the "const object"...


Regards,

Niels Dekker
www.xs4all.nl/~nd/dekkerware
 
P

Phlip

Niels said:
Of course, it only catches a non-const copy of the "const object"...

Could someone cite /Effective C++/ by my tonsorial idol, Scott Meyers, here?
I think he covers a rationale for 'const'.

I'm to lazy to get my copy out of the closet...
 
N

Niels Dekker - no reply address

Phlip said:
Could someone cite /Effective C++/ by my tonsorial idol, Scott Meyers,
here? I think he covers a rationale for 'const'.

Do you mean "Item 21: Use const whenever possible"? It doesn't say
anything specific about catching exceptions as "const".

Or do you mean "Item 13: Catch exceptions by reference", from /More
Effective C++/? To my surprise, it suggests catching exceptions by
non-const reference!

More Effective C++, Item 13 says:

try {
someFunction(); // no change here
}
catch (exception& ex) { // here we catch by reference
// instead of by value


cerr << ex.what(); // now calls
// Validation_error::what(),
... // not exception::what()
}

(Cited from the Effective C++ CD, third pressing, June 1999.)


BTW, I'm a big fan of Scott Meyers as well, don't get me wrong! :)


Niels Dekker
www.xs4all.nl/~nd/dekkerware
 
F

fabio de francesco

Gregg said:
(e-mail address removed) (fabio de francesco) wrote in


You are catching by value instead of by reference. This will result in a
copy being made of the thrown object, just as it would if you passed it
to a function by value.

If it is "...just as it would if you passed it to a function by
value." why passing an object to a function by value makes only a
single copy, whereas we have two copies with "throw-catch"?
Instead of

catch (Object obj)

try

catch (const Object& obj)

I already know. This is not the point.
The first copy is made when you are throwing the non-temporary local
object.

And the second one?

OK, I am sure that because of my poor English I wasn't able to explain
what is the question I would like to have an answer for.

I hate to repeat myself but may be this time I will be able to better
explain the issue.

I know that I'm passing by value instead of by reference. I already
know how to avoid unneeded copies (more than one copy in this case)
with const object reference or similar.

What interests me is to understand why the throw-catch mechanism needs
to copy the object twice, whereas the similar simple passing of an
object by value does a unique single copy of the original.

I have put a call to the function "void fun_1(Object obj)" with the
intention to show a contrast to the other case when I have two copies
(passing by value too).

Please look carefully at lines #6 and #8 of the program output:

1) [myself@myhost /temporary]# ./exception
2) Object constructor of main object
3) Copy constructor for main object (copy)
4) Object destructor of main object (copy)
5) Object constructor of local object
6) Copy constructor for local object (copy)
7) Object destructor of local object
8) Copy constructor for local object (copy) (copy)
9) Caught exception from local object (copy) (copy)
10) Object destructor of local object (copy) (copy)
11) Object destructor of local object (copy)
12) Object destructor of main object

What I see is a first copy-construction of the local object (#6) in
fun_2() then the destruction of the local object (#7), that is OK, and
eventually a second copy-construction of the same local object. These
objects are destroyed at lines #10 and #11.
This behaviour constrasts to what we see at #3 where only a single
copy-construction is made when passing an object by value to fun_1().

Thank you all. I am sorry for all the repetitions of the same concepts
I had to make.

Ciao,
Fabio De Francesco
 
G

Gregg

(e-mail address removed) (fabio de francesco) wrote in
If it is "...just as it would if you passed it to a function by
value." why passing an object to a function by value makes only a
single copy, whereas we have two copies with "throw-catch"?

It is only one in each case. The second copy in your example (the first
chronologically) occurs because in your fun_2 function you are creating
throwing in two discrete steps rather than one.
I already know. This is not the point.


And the second one?

The second one is the catch by value. If you did this

throw Object("local object");

rather than

Object Thrown("local object");
throw Thrown;

I think you would see only one copy construction.

Gregg
 
F

fabio de francesco

Gregg said:
(e-mail address removed) (fabio de francesco) wrote in


It is only one in each case. The second copy in your example (the first
chronologically) occurs because in your fun_2 function you are creating
throwing in two discrete steps rather than one.


The second one is the catch by value. If you did this

throw Object("local object");

rather than

Object Thrown("local object");
throw Thrown;

I think you would see only one copy construction.

Gregg

Thank you Gregg,
this is exactily what triggered the second copy-construction. I didn't
imagine that splitting the creation of the object to be thrown in two
discrete steps would had required the use of two different
constructions; for this reasons I imagined that the second copy was
made by the thrown-catch mechanism itself.

Ciao,
Fabio De Francesco
 

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

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,233
Latest member
AlyssaCrai

Latest Threads

Top