3

I started using std::exception as a base class for all my exceptions recently. I could not properly override what() without putting the virtual keyword in front of it. Without the virtual keyword it always seemed to call the what() function of the base class, std::exception.

It perplexed me a little because I thought that one never needed to put virtual in front of a function when overriding it (and here is a post that seems to confirm that). But I decided to let it go and move on.

Then today while reading O'Reilly's "Safe C++" I found the author also overriding what() with the virtual keyword. He wrote...

virtual const char* what() const throw () { /* stuff */ }

Why is he overriding a function and using the virtual keyword? Is it just for "documentation" as suggested in the post I quoted above?

Community
  • 1
  • 1
John Fitzpatrick
  • 4,207
  • 7
  • 48
  • 71
  • 1
    `override` would be better documentation, as it would give a compiler error if you typo'd the signature, but yes, afaik, it's just being clear that you're overriding it. – chris Nov 06 '12 at 19:27
  • 3
    It's true that you don't strictly need the `virtual` keyword when overriding. Without any code, I'm going to guess that your problem stems from [object slicing](http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c) - i.e. you're catching exceptions by value: ˙`catch (std::exception e)`. – jrok Nov 06 '12 at 19:28
  • 2
    @chris: The `override` is a Microsoft-specific extension, although it's still useful as documentation if it's `#define`d to nothing in non-Microsoft compilers. – Adam Rosenfield Nov 06 '12 at 19:29
  • @Chris: thanks so I will assume that I didn't really need to use `virtual` when I overrode `what()` and my problem was really something else that was solved accidentally. – John Fitzpatrick Nov 06 '12 at 19:29
  • 6
    @AdamRosenfield, It's part of C++11. `const char *what() const override {}` and the `throw` is deprecated in C++11, but I forget what to use. Maybe `noexcept` or something. – chris Nov 06 '12 at 19:29
  • @chris: Don't forget `noexcept` / `throw()`. – Xeo Nov 06 '12 at 19:30
  • @Xeo, Yeah, I was trying to remember what it was replaced with. – chris Nov 06 '12 at 19:31
  • @chris: Oh thanks, good to know; I haven't started seriously using C++11 for anything other than tests/toys. – Adam Rosenfield Nov 06 '12 at 19:31
  • @AdamRosenfield, Likewise, I didn't know it was a Microsoft extension at all. That's good to know. – chris Nov 06 '12 at 19:32
  • @jrok: Unfortunately the non-working code is no longer available to me, as it now works. But perhaps you are right. I tried a dozen things to fix the problem when I couldn't override `what()` and I might have fixed it by resolving the slicing issue (accidentally). – John Fitzpatrick Nov 06 '12 at 19:32
  • @chris: Yep, it's a borrowed feature from C#/.NET that's been supported in native code as far back as [Visual Studio 2005](http://msdn.microsoft.com/en-us/library/vstudio/z8ew2153%28v=vs.80%29.aspx), I believe. – Adam Rosenfield Nov 06 '12 at 19:37

1 Answers1

3

You do not have to put the virtual keyword in front of your override of what() in order to call the subclass implementation. Perhaps when you discovered that it was invoking the base class implementation the exception object you were referencing had been sliced via an inappropriate pass of the exception? For example, I always catch by reference (per Scott Meyers' recommendation), but if I caught by the exception value and declared the catch as a superclass of the subclass that might be thrown, then the object would be sliced when I caught it. In other words, if I had this exception subclass declared:

class my_exception : public std::exception
...

and I caught an instance of it like so:

try
{
    ...
    throw my_exception("Some message");
}
catch (std::exception e)
{
    ...
}

e in the catch block would be a sliced object. You should catch an exception like this:

try
{
    ...
    throw my_exception("Some message");
}
catch (std::exception& e)
{
    ...
}
Christian Rau
  • 45,360
  • 10
  • 108
  • 185
ScoPi
  • 1,193
  • 9
  • 14
  • 1
    Or rather like this: `const std::exception&`. But othwerwise correct answer of course and pretty likely to be the cause of his error. Though, it's *"slicing"* instead of *"splicing"*, because you're slicing the object into its subparts. – Christian Rau Nov 07 '12 at 13:31
  • Fair enough, but I'm not sure that the const really adds anything here and isn't mentioned as part of Scott Meyers' point about catching exceptions by reference. Thrown exceptions aren't like function/method parameters, so catching by const doesn't add anything (i.e. the compiler isn't going to prevent you from throwing an exception because the catch isn't const). – ScoPi Nov 08 '12 at 08:59
  • Are you desperately missing it? No! Does it cause any harm? No! Does it make the intent of catching an exception and not modifying it (which would be pretty strange) clearer? Probably! In fact when I see something passed in as a non-const reference (be it a function call or whatever) I wonder if the argument is to be changed and why. Of course you're right in that a non-const reference will probably not do much harm here, but well, why use a non-const reference when you can use a const reference? – Christian Rau Nov 08 '12 at 09:03