Exceptions are for exceptional cases
I'm trying to learn exception handling in C++.
This is a very bad example to learn exceptions. They are meant for situations where you cannot know that there is something wrong. For example if you want to use std::vector<int>
but you're out of memory:
std::vector<int> vec(4000000000LL); // will likely throw std::bad_alloc
I'll add an exercise for you at the end of this review. But let's have a look at your code.
A review of your code
You're not using using namespace std
, which is a great plus.
However, you include <stdexcept>
, which isn't necessary here. You don't use any of the standard exception, instead, you throw 0
. However, system
is in <cstdlib>
. You should include that one instead.
Next, naming.
int i1, i2;
Those names don't have any meaning. What is i1
? What is i2
? Naming is hard, but it's important. You're going to use them as numerator and denominator, or as dividend and divisor, so call them appropriately:
int numerator, denominator;
Getting rid of goto
Now to your try
block. Don't. Use. goto
. You want to repeat the block until you didn't get 0
. We can do that with a simple while
:
int main (){
int numerator, denominator;
cout << "Give two integers" << endl;
cin >> numerator;
// continue forever (1)
while(true) {
try {
cin >> denominator;
if (denominator == 0)
throw 0; // this will "go to" catch (2)
cout << numerator / denominator << endl;
break; // we didn't throw, we can stop (3)
}
catch (...) {
cout << "Second integer cannot be zero , please enter a valid integer" << endl;
}
}
system("pause");
return 0;
}
A "clean" version without exceptions
However, that's not how I would write the program, since exceptions are meant for exceptional cases. I would write
#include <iostream>
#include <cstdlib>
int main() {
int numerator, denominator;
std::cout << "Please enter two integers." << std::endl;
std::cin >> numerator >> denominator;
while(denominator == 0) {
std::cout << "Second integer cannot be zero. Try again." << std::endl;
std::cin >> denominator;
}
std::cout << (numerator / denominator) << std::endl;
system("pause");
return 0;
}
As you can see, exceptions aren't really necessary to keep the user from entering a zero. Furthermore, the control flow is a lot easier to read.
Note that system("pause");
won't work on Linux, since there is no application called pause
. If possible, try to use your IDEs features to keep your console window alive even after your program finished, or have the output logged somewhere, or use it in the CMD/PowerShell.
Exception exercise
We need a better exercise for your exceptions. How about this?
int divide(int numerator, int denominator) {
if(denominator == 0)
throw "divide: division by zero";
return numerator / denominator;
}
This is a valid place for an exception. A function has only a single return value, so it can only tell you that something is wrong with an exception*.
Exercise: Try to use that function in your code and handle the exception. Also show the exception to the user, they're probably interested in what went wrong.
You should use the code similar to
std::cout << divide(numerator, denominator) << std::endl;
that is you don't check the denominator
. Note that this is still a contrived example.
* Technically, that's not true. You can use references, pointers, or wrap the return value in some struct
/variant, but let's keep things simple
goto
s as a question of policy, or coding habits - you often can't usegoto
s, or rather agoto
might mess up your program. I'm not sure if that's the case for going-to from the catch to the try block, but honestly - if in doubt, I just wouldn't try it. You might find what you're "learning" is undefined compiler-specific and platform-specific behavior. \$\endgroup\$goto
orswitch
statement shall not be used to transfer control into atry
block or into a handler.. So this program exhibits undefined behaviour. \$\endgroup\$