C++ / CHAPTER 12
Exception safety and committing changes
AdvancedMake failure leave resources and state in a defined condition.
By the end of this chapter
- Distinguish cleanup from state rollback
- Build a replacement before committing
- Catch only errors an operation can handle
The idea
Stack unwinding destroys fully constructed automatic objects as an exception leaves their scopes, making RAII useful for cleanup. Cleanup alone does not undo mutations already made to an existing object. An algorithm that promises to leave its input unchanged on failure must arrange its operations accordingly rather than assuming exceptions provide transactions.
One common strategy is to build a replacement in local owned storage, perform all potentially failing validation and construction there, and commit with an operation whose failure properties are understood. Copying a vector before appending protects the original if construction throws. Catch exceptions where recovery or useful context is possible; catching everything and returning a success-looking value can hide both invalid input and implementation defects.
#include <iostream>
#include <vector>
#include <stdexcept>
void appendPositive(std::vector<int>& values, int n) {
if (n <= 0) throw std::invalid_argument("positive required");
auto next = values;
next.push_back(n);
values.swap(next);
}
int main() {
std::vector<int> v{2};
try { appendPositive(v, -1); } catch (const std::invalid_argument&) {}
std::cout << v.size() << ' ' << v[0];
}1 2
Validation and replacement construction occur before the swap commits the new vector.
Try it yourself
Design a batch update that rejects an invalid item without partly modifying the caller’s vector.
Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.
CHECK YOUR UNDERSTANDING