C++ Tutorial
C++ Exceptions
throw signals a failure. try and catch recover without scattering error codes through every call.
A failure that jumps to a handler
Some functions cannot do their job: divide by zero, a missing setting, a value outside the legal range. You can return a special code from every function on the path. Or you can throw an exception and let acatch block higher up handle it.
This chapter uses runtime_error from <stdexcept>. Include that header. Catch the standard base type with catch (const exception& e) so you can print e.what().
throw runtime_error
throw stops the current function. The object you throw travels up the call stack until a matchingcatch takes it. If nothing catches it, the program terminates.
Example
#include <iostream>
#include <stdexcept>
using namespace std;
int divide(int a, int b) {
if (b == 0) {
throw runtime_error("divide by zero");
}
return a / b;
}
int main() {
try {
cout << divide(10, 2) << endl;
cout << divide(10, 0) << endl;
} catch (const exception& e) {
cout << e.what() << endl;
}
return 0;
}The first call prints 5. The second throws. main catches it and printsdivide by zero. The program still returns 0. Without try, that second call would abort.
try and catch
Put the risky work in try. Put recovery in catch. Execution enters the catch only if something was thrown. After the catch finishes, the function continues as normal.
Example
#include <iostream>
#include <stdexcept>
using namespace std;
int main() {
try {
throw runtime_error("disk full");
} catch (const exception& e) {
cout << "recovered: " << e.what() << endl;
}
cout << "still running" << endl;
return 0;
}Output is two lines. The exception was not fatal. That is the difference from an uncaught error: you choose a message, a fallback value, or a clean exit.
catch (const exception&)
Catch by const reference. That avoids copying the exception object and lets derived types such asruntime_error and invalid_argument match the base exception.e.what() returns a C string that describes the failure.
| Piece | Role |
|---|---|
| try | Code that might throw |
| throw runtime_error("...") | Build and send the error |
| catch (const exception& e) | Handle any standard exception |
| e.what() | Human-readable reason |
Catching exception does not catch everything. A throw 1; or a throw of some other unrelated type would miss this handler. Stick to std::exception types in this tutorial.
Validate, then throw
A setter or a helper can throw when the caller passes a bad value. The caller wraps the call in try/catch instead of checking a magic return code after every line.
Example
#include <iostream>
#include <stdexcept>
using namespace std;
int percent(int n) {
if (n < 0 || n > 100) {
throw runtime_error("percent out of range");
}
return n;
}
int main() {
try {
cout << percent(40) << endl;
cout << percent(140) << endl;
} catch (const exception& e) {
cout << e.what() << endl;
}
return 0;
}40 prints. 140 does not reach return n. The catch prints the message fromthrow.
When not to throw
- Do not throw for expected, everyday cases such as “the user typed a letter.” Ask again, or return a bool.
- Do not use exceptions as a second return value for success. They are for failures you cannot handle locally.
- Keep
tryblocks short so it is obvious which call can fail.
These examples compile at /cpp/try. They do not need a file on disk. Next: a resizable array —vector.
Worked examples
The short listings above are there so you can see the grammar. The programs here use the same statements on quantities that already have units: a speed, a pH, a count of bases. They are classroom numbers. Air resistance is ignored. g is 9.81 m/s² unless a line says otherwise.
Open them in the C++ editor at /cpp/try. Change one measurement and check whether the result still has the right unit.
Physics
A negative mass is not a mass
Mass is positive. throw is how a function refuses −2 kg instead of computing a negative kinetic energy and hoping someone notices. catch prints the reason.
KE = ½ m v²
Example
#include <iostream>
#include <stdexcept>
using namespace std;
double kinetic_energy(double mass, double speed) {
if (mass < 0.0) throw invalid_argument("mass must be >= 0");
return 0.5 * mass * speed * speed;
}
int main() {
try {
cout << kinetic_energy(-2.0, 3.0) << endl;
} catch (const exception &err) {
cout << err.what() << endl;
}
return 0;
}Maths
Divide by zero as a throw
Mean is sum / n. n = 0 is not a class; it is an empty list. Throwing is clearer than returning NaN and hoping the next line checks it.
mean = Σx / n
Example
#include <iostream>
#include <stdexcept>
using namespace std;
double mean(double sum, int n) {
if (n == 0) throw invalid_argument("empty sample");
return sum / n;
}
int main() {
try {
cout << mean(24.0, 0) << endl;
} catch (const exception &err) {
cout << err.what() << endl;
}
return 0;
}