Introduction
Things go wrong in every real program, and data has to outlive it. C++ handles both with tools that fit together well: exceptions separate the error path from the normal path, and file streams work just like cin and cout.
How it works
Throwing. throw stops the current function and looks for a matching catch up the call stack. If none is found, the program ends.
Standard exceptions. <stdexcept> has types such as runtime_error, invalid_argument and out_of_range, each carrying a message that what() returns. Library functions use them too: v.at(i) throws out_of_range, and stoi throws invalid_argument.
Catching. Catch by const reference. A catch for a base type such as exception also catches all the types derived from it, so put specific handlers first.
#include <iostream>
#include <stdexcept>
using namespace std;
int divide(int a, int b) {
if (b == 0) {
throw invalid_argument("cannot divide by zero");
}
return a / b;
}
int main() {
try {
cout << divide(10, 2) << endl;
cout << divide(1, 0) << endl;
cout << "not reached" << endl;
} catch (const invalid_argument& e) {
cout << "error: " << e.what() << endl;
}
cout << "done" << endl;
return 0;
}Files. <fstream> provides ifstream for reading and ofstream for writing. They use the same >>, << and getline as the console.
Automatic closing. A stream closes its file when the stream object goes away, even if an exception occurs. This technique is called RAII, and it is central to modern C++.
A failed open does not throw by default. Test the stream instead: if (!in).
Files in StepVioz exist in memory while the program runs. The same code writes real files when compiled on your own computer.
Syntax
try {
risky();
} catch (const exception& e) {
cout << e.what() << endl;
}
ifstream in("data.txt");
if (!in) { ... }
Example
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main() {
ofstream out("notes.txt");
out << "first 10" << endl;
out << "second 20" << endl;
out.close();
ifstream in("notes.txt");
string word;
int n, total = 0;
while (in >> word >> n) {
total += n;
}
cout << "total " << total << endl;
ifstream missing("nope.txt");
if (!missing) {
cout << "could not open" << endl;
}
return 0;
}Line by line
ofstream out("notes.txt")creates the file and opens it.- Two lines are written with
<<, just as withcout. ifstream inopens the file for reading.while (in >> word >> n)reads pairs until the file ends.- Opening a missing file does not stop the program, and
!missingdetects it.
Expected result
total 30
could not open
In the real world
Servers catch exceptions for each request, so that one bad request cannot bring down the rest. Programs save their settings, logs and documents with file streams.
Common mistakes
- Catching by value, which copies the exception and can lose details.
- Not checking whether a file opened.
- Catching a base type before a derived type, which makes the second handler unreachable.
- Using exceptions for ordinary events, such as reaching the end of a loop.
Best practices
- Throw standard exception types, or types derived from them.
- Catch errors where you can do something useful about them.
- Let streams close themselves by keeping them in a small scope.
Summary
throw reports a problem and catch handles it, with standard types such as invalid_argument carrying messages. File streams work just like cin and cout, and close themselves automatically.
Quick check
Key takeaways
- Throw when a function cannot do its job.
- Catch by const reference.
- File streams close themselves when they go out of scope.
Practice problems for this lesson
My notes
Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.