I have the following code:
class foobar_error: public std::logic_error {
public:
foobar_error() : std::logic_error("Very bad things happened!") {}
};
int main() {
try {
throw foobar_error();
}
catch(const std::exception& e) {
std::cout << e.what() << std::endl; // "Very bad things happened!"
}
return 0;
}
The program prints the expected string "Very bad things happened!".
One of my colleagues caught a foobar_error by value:
try {
throw foobar_error();
}
catch(std::exception e) {
std::cout << e.what() << std::endl; // "St9exception"
}
The result was, that the program printed "St9exception" (no typo - there's
really a '9' in the string). I knew that catching an exception by reference
is more efficient because the exception doesn't need to be copied. But
there also seems to be a slicing problem here. Is that correct behaviour?
We're using g++ 3.2.2. Catching by value works fine if we use
std::logic_error instead of std::exception:
catch(std::logic_error e) {
std::cout << e.what() << std::endl; // "Very bad things happened"
}
Any comments?
Ralph
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Ralph
|
10/7/2003 6:31:48 PM |
|
"Ralph Peterson" wrote:
> I have the following code:
>
> class foobar_error: public std::logic_error {
> public:
> foobar_error() : std::logic_error("Very bad
> things happened!") {}
> };
>
> int main() {
> try {
> throw foobar_error();
> }
> catch(const std::exception& e) {
> std::cout << e.what() << std::endl; // "Very
> bad things happened!"
> }
> return 0;
> }
>
> The program prints the expected string "Very bad
> things happened!".
>
> One of my colleagues caught a foobar_error by value:
>
> try {
> throw foobar_error();
> }
> catch(std::exception e) {
> std::cout << e.what() <<
> std::endl; // "St9exception"
> }
>
> The result was, that the program
> printed "St9exception" (no typo -
> there's really a '9' in the string). I knew that
> catching an exception
> by reference is more efficient because the
> exception doesn't need to
> be copied. But there also seems to be a slicing
> problem here.
Yes, there certainly is.
> Is that
> correct behaviour?
Yes. The program called the std::exception::what()
member function, which differs from the
std::logic_error::what() member function.
> We're using g++ 3.2.2. Catching by value works fine
> if we use std::logic_error instead of
> std::exception:
>
> catch(std::logic_error e) {
> std::cout << e.what() << std::endl; // "Very
> bad things happened"
> }
>
Actually, you have a slicing problem in your last
example too, it just doesn't manifest itself in this
particular case. Here's what's going on: everything
derived from std::exception has a virtual member
function called "what()". std::exception provides a
default definition that is used if a derived class
does not override the virtual vunction.
std::logic_error overrides std::exception's what()
virtual function. It also includes a std::string
member variable in which it stored your message, while
std::exception does not have that member variable. If
you wanted, you could override the what() virtual
function again in your foobar_error class. If you had
done so, then your three examples could have printed
three different messages.
> Any comments?
Yep. Always catch exception classes derived from
std::exception and its progeny by reference, not by
value, or you will get this slicing behavior.
Your examples also demonstrate the wisdom of Scott
Meyers' advice to always make non-leaf classes
abstract. That prevents slicing problems like this
from occurring because the compiler will flag them as
errors. Too bad we can't change the standard library
to follow that advice.
Best regards,
Tom
__________________________________
Do you Yahoo!?
The New Yahoo! Shopping - with improved product search
http://shopping.yahoo.com
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Thomas8675309 (484)
|
10/7/2003 7:14:47 PM
|
|
Ralph Peterson wrote:
>
> The program prints the expected string "Very bad things happened!".
>
> One of my colleagues caught a foobar_error by value:
>
> try {
> throw foobar_error();
> }
> catch(std::exception e) {
> std::cout << e.what() << std::endl; // "St9exception"
> }
>
The code catches an std::exception by value. Read about "slicing".
--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Pete
|
10/7/2003 8:02:53 PM
|
|
"Pete Becker" <petebecker@acm.org> wrote in message
news:3F830F10.3BD377F@acm.org...
> Ralph Peterson wrote:
> > try {
> > throw foobar_error();
> > }
> > catch(std::exception e) {
> > std::cout << e.what() << std::endl; // "St9exception"
> > }
> >
>
> The code catches an std::exception by value. Read about "slicing".
It's just like passing a foobar_error to a function that takes a
std::exception by value.
--
+++++++++++
Siemel Naran
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Siemel
|
10/8/2003 6:21:59 AM
|
|
Pete Becker wrote:
> Ralph Peterson wrote:
>>
>> The program prints the expected string "Very bad things happened!".
>>
>> One of my colleagues caught a foobar_error by value:
>>
>> try {
>> throw foobar_error();
>> }
>> catch(std::exception e) {
>> std::cout << e.what() << std::endl; // "St9exception"
>> }
>>
>
> The code catches an std::exception by value. Read about "slicing".
I already said that I think this is a slicing problem.
I was just surprised that the base class std::exception has the member
function what() but does obviously not hold the what()-string.
I assumed that std::logic_error's ctor passes the string on to
std::exception, which does not seem to be the case. I was wondering if this
behaviour is standards compliant.
Best regards
Ralph
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Ralph
|
10/8/2003 6:27:09 PM
|
|
Ralph Peterson <ralph.peterson@gmx.net> wrote in
news:bm0hgs$gpb7n$1@news.hansenet.net:
> I was just surprised that the base class std::exception has the member
> function what() but does obviously not hold the what()-string.
>
> I assumed that std::logic_error's ctor passes the string on to
> std::exception, which does not seem to be the case. I was wondering if
> this behaviour is standards compliant.
Think std::bad_alloc. Should it pass a std::string to std::exception?
Where does the memory for that string come from?
std::exception::what() is virtual, and retursn const char* with good
reason. If all implementations simply set a std::string member, what()
could have been a public data member.
HTH,
--
Michiel Salters
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Michiel
|
10/8/2003 10:09:08 PM
|
|
Ralph Peterson <ralph.peterson@gmx.net> wrote:
> I was just surprised that the base class std::exception has the member
> function what() but does obviously not hold the what()-string.
Actually, it does not hold any data member at all - well, at least it
is not required to do so. 'what()' is a virtual function and the
derived classes need to provide this message. Often, they can do so
without allocating any memory - which is crucial for example to
things like 'std::bad_alloc' which is thrown when the system has run
out of memory.
> I assumed that std::logic_error's ctor passes the string on to
> std::exception, which does not seem to be the case. I was wondering if this
> behaviour is standards compliant.
Yes, this is standard compliant and it was a deliberate descision.
--
<mailto:dietmar_kuehl@yahoo.com> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
dietmar_kuehl
|
10/9/2003 8:49:51 PM
|
|
"Michiel Salters" <Michiel.Salters@cmg.nl> wrote in message
> Ralph Peterson <ralph.peterson@gmx.net> wrote in
> > I was just surprised that the base class std::exception has the member
> > function what() but does obviously not hold the what()-string.
Correct. The base class has no data.
> > I assumed that std::logic_error's ctor passes the string on to
> > std::exception, which does not seem to be the case. I was wondering if
> > this behaviour is standards compliant.
Try this program
class foobar_error: public std::logic_error {
public:
foobar_error() : std::logic_error("Very bad things happened!") {}
};
int main() {
try {
throw foobar_error();
}
catch(const std::runtime_error& e) {
std::cout << e.what() << std::endl;
}
return 0;
}
> Think std::bad_alloc. Should it pass a std::string to std::exception?
> Where does the memory for that string come from?
>
> std::exception::what() is virtual, and retursn const char* with good
> reason. If all implementations simply set a std::string member, what()
> could have been a public data member.
Also, there is the problem of an out-of-memory exception when you are about
to throw an exception. For example
throw foobar_error();
The statement literally means constructs a foobar_error object, then copy
this object into the exception space. In copying the object to the
exception space it would invoke std::string::string(const string&) which
could throw an out-of-memory error.
We had a long thread about this issue once, but I don't recall the subject
heading (and I'm offline now so can't look it up).
--
+++++++++++
Siemel Naran
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Siemel
|
10/10/2003 4:18:32 PM
|
|
|
7 Replies
383 Views
(page loaded in 0.116 seconds)
|