0

I'm just learning how to use exceptions in C++ and have come across weird behavior in my "test" code. (excuse overly stupid questions like this one, please...it's NOT lack of research/effort, just lack of experience!) If I'm catching just the exception DivideByZero it works fine.

But introducing the second exception StupidQuestion makes the code not work exactly how I expected. How I wrote it below I thought it should take care of the DivideByZero exception if it needs to, and if not then check if StupidQuestion occurs, and if not just go back to the try clause and print the normal result. But if I input, say, a=3 and b=1, the program redirects to the DivideByZero try clause instead of the StupidQuestion one. The weird thing is, though, divide does seem to be throwing StupidQuestion (see via cout statement), but it's not catching right, as also seen by the absense of the cout statement.

#include <iostream>
#include <cstdlib>
using namespace std;
const int DivideByZero = 42;
const int StupidQuestion=1337;
float divide (int,int);
main(){
       int a,b;
       float c;
       cout << "Enter numerator: ";
       cin >> a;
       cout << "Enter denominator: ";
       cin >> b;
       try{
           c = divide(a,b);
           cout << "The answer is " << c << endl;
           }
       catch(int DivideByZero){
                           cout << "ERROR: Divide by zero!" << endl;
                           }
       catch(int StupidQuestion){
                                 cout << "But doesn't come over here...?" << endl;
                             cout << "ERROR: You are an idiot for asking a stupid question like that!" << endl;
                             }
       system("PAUSE");
       }

float divide(int a, int b){
      if(b==0){
               throw DivideByZero;
               }
      else if(b==1){
               cout << "It goes correctly here...?" << endl;
               throw StupidQuestion;
               }
      else return (float)a/b;
}

I was wondering if it had something to do with the fact that DivideByZero and StupidQuestion were both of type int, so I changed the code to make StupidQuestion be of type char instead of int. (So: const char StupidQuestion='F'; and catch(char StupidQuestion) were really the only things changed from above) And it worked fine.

Why isn't the above code working when the two exceptions have the same type (int)?

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 3
    The exceptions are caught by type not by name. So both are type int and the first handler is going to catch them. If you want to differentiate them make the second one a different type. – Duck Oct 24 '13 at 16:37
  • @Duck: This would be the answer I'd upvote. – Sebastian Mach Oct 24 '13 at 16:41
  • 1
    Any reason not to create classes that inherit from exception and use those instead? That follows the pattern better than throwing random types – clcto Oct 24 '13 at 16:41

3 Answers3

3
catch(int DivideByZero)   { }
catch(int StupidQuestion) { }

Both catch blocks catch ints, they're just named differently. Only the first one can ever be entered, the second one is dead code.

jrok
  • 54,456
  • 9
  • 109
  • 141
  • What's the point of naming them (like I saw in the tutorial) if they only do by type? Then I would have to declare/use a new type for every catch clause??? which seems kind of long winded... –  Oct 24 '13 at 16:41
  • @Thornshadow17432: Link the tutorial, we need more context for this. – Jonas Schäfer Oct 24 '13 at 16:42
  • @Thornshadow17432 its like calling a function. it passes the value to you and it is stored in that variable so you can use it in the block. such as `catch( string ex ){ cerr << ex << endl; }` – clcto Oct 24 '13 at 16:43
  • 2
    The point of naming them is so that you can refer to them by name, just as is the point of naming any other variable in your program. That's especially important since, as you've now learned, we catch things by type, not by specific value. Multiple distinct exceptions can all have different values but the same type. You'll want to read their values to determine details about them. This becomes more valuable when you start throwing *classes* since you get to use inheritance to catch objects of many related types. – Rob Kennedy Oct 24 '13 at 16:47
  • 1
    @Thornshadow17432 “which seems kind of long winded” – but it’s not in practice. It turns out that you don’t need terribly many different exception types, it’s mostly the same ones recurring. – Konrad Rudolph Oct 24 '13 at 16:47
  • @Thornshadow17432 Just to be clear, it's not the catch clause that's being named. It's the variable that's being named. – Darius Makaitis Oct 24 '13 at 16:50
3

Instead of this

catch(int DivideByZero) {
    cout << "ERROR: Divide by zero!" << endl;
}
catch(int StupidQuestion) {
    cout << "But doesn't come over here...?" << endl;
    cout << "ERROR: You are an idiot for asking a stupid question like that!" << endl;
}

you are looking for

catch (int errval) {
    if (errval == DivideByZero) {
        cout << "ERROR: Divide by zero!" << endl;
    }
    else if (errval == StupidQuestion) {
        cout << "ERROR: You are an idiot for asking a stupid question like that!" << endl;
    }
    else {
        throw; // for other errors, keep searching for a handler
    }
}

The variable name inside the catch clause is creating a new local variable, which has no relation to a global constant with the same name.

Also note that there will be no way to catch just one error number... but you can rethrow unknown errors as I show.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
0

When choosing a handler for an exception only type is taken into account, and neither values nor addresses (addresses of variables are not applicable here at all because of how exceptions work), also names of variables do not exist after compilation. The first appropriate handler for the exception is always chosen.

Please look my answer to another question for details: https://stackoverflow.com/a/45436594/1790694