6

I've just created an exception hierarchy and want my catch-block to show the message of the derived exception. I got 5 exceptions like this one:

class ImagetypeException : public TGAException {  
public:  
const char* what() const throw();  
};  


const char* ImagetypeException::what() const throw() {
    return "Der Bildtyp ist nicht \"RGB unkomprimiert\".";
}

All of them are derived from TGAException, that is derived from std::exception.

class TGAException : public std::exception {
public:
    virtual const char* what() const throw();
};

const char* TGAException::what() const throw() {
    return "Beim Einlesen oder Verarbeiten der TGA-Datei ist ein unerwarteter Fehler aufgetreten!";
}

So I obviously want to throw these at some point in my code and thought it might be a good idea, to minimize the amount of catch-blocks I need.

catch (TGAException e) {
        cout << e.what() << endl;
    }

If I do it like this, the message, that will be printed, is the one from TGAException, but I want it to show the more specific derived messages. So what excatly do I need to do to get this to work the way I want it to?

Awesome36
  • 89
  • 7
  • 4
    Catch by reference, as in `catch (TGAException& e)`. When you catch by value, you [slice it](https://en.wikipedia.org/wiki/Object_slicing) – Igor Tandetnik Jan 12 '17 at 15:58
  • 2
    better yet, catch by const reference since you are not planning to change the exception object. @IgorTandetnik you should make this an answer (or I will but I'm not trying to steal rep) – Dale Wilson Jan 12 '17 at 16:01
  • 1
    You're doing *Object Slicing*, until you catch by reference. – A.S.H Jan 12 '17 at 16:07
  • 1
    Actually `"Der Bildtyp ist nicht \"RGB unkomprimiert\"."` seems to match `std::invalid_argument`, are you sure that you need your own exception classes? – Kamil Koczurek Jan 12 '17 at 16:08

1 Answers1

9

When you catch like this:

catch (TGAException e) {
    cout << e.what() << endl;
}

The compiler makes a copy of the original exception and assigns it to e. It uses the TGAException copy constructor so the exception seen inside the catch block is not an ImagetypeException, it is a TGAException. This phenomenon is called object slicing.

If you catch it this way:

catch (const TGAException & e) {
    cout << e.what() << endl;
}

No copy is needed and it will work the way you expect it to.

As a general guideline: Always catch exceptions by reference, and almost always catch them by const reference.

Dale Wilson
  • 9,166
  • 3
  • 34
  • 52
  • thanks! very helpful and quick answer :) but a noob question in the first place -_-' – Awesome36 Jan 12 '17 at 16:09
  • 1
    Nope. A good question. You'd be amazed at how many experienced C++ programmers get tripped up over object slicing (note: it can happen on method calls when parameters and return values are passed by value, or even assignments when the target is not a pointer or reference.) – Dale Wilson Jan 12 '17 at 16:53