例外処理: 緊急脱出ボタン

予期しないエラーを適切に処理。RAIIと組み合わせて安全に。

例外
実行フローを中断しエラーを通知する仕組み。
RAII
リソースをオブジェクトのライフタイムに紐付ける。
noexcept
例外を投げないことを保証するキーワード。

例外とは?

火災報知器 (Fire Alarm)

戻り値でのエラーチェックは「ドアごとの点検」です。面倒で忘れがちです。例外は「火災報知器」です。異常(火事)が起きるとベルを鳴らし(throw)、全員が作業を中断して(スタック巻き戻し)、避難場所(catch)まで一気に脱出します。

例外は「無視できないエラー」を扱うのに適しています。戻り値は無視されることがありますが、例外は誰かがキャッチしなければプログラムが終了します。ただし、頻繁に起きる通常の分岐(例:ファイルがない)には、例外ではなく戻り値(std::optionalなど)を使うのが好ましいです。

try-catch
#include <stdexcept>
#include <memory>
#include <fstream>
#include <iostream>
void processFile(const std::string& path) {
std::ifstream file(path);
if (!file) {
throw std::runtime_error("Cannot open: " + path);
}
}
int main() {
try {
auto ptr = std::make_unique<int>(42); // RAII
processFile("data.txt");
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << '
';
}
}
実行結果
Error: Cannot open: data.txt

RAII: 例外安全の鍵

例外が投げられると、関数は途中で終了します。もし `delete` や `close` の前に例外が起きると、リソースリークが発生します。RAII(スマートポインタなど)を使えば、例外発生時でもデストラクタが確実に呼ばれ、リソースが解放されます。

Bad
// ❌ Bad: 生ポインタ + 例外
void risky() {
int* data = new int[100];
processFile("data.txt"); // ここで例外が投げられたら?
delete[] data; // ← ここに到達しない = メモリリーク!
}
Good
// ✅ Good: RAII + スマートポインタ
void safe() {
auto data = std::make_unique<int[]>(100);
processFile("data.txt"); // 例外が投げられても...
// data は自動で解放される(RAIIの力)
}

noexcept

noexcept Specifier
// noexcept: 例外を投げないことを保証
void swapData(Data& a, Data& b) noexcept {
std::swap(a.ptr, b.ptr);
}
// ムーブコンストラクタは noexcept が重要
// これがないと std::vector が効率的なリサイズを行えない
MyClass(MyClass&& other) noexcept
: data_(std::move(other.data_)) {}
Tip: ムーブコンストラクタは noexcept にすると std::vector が効率的に動作。

合格ライン

try-catch-throw を書ける
RAII が例外安全に重要な理由を説明できる

参考リンク

演習課題

課題1: カスタム例外
std::exception を継承したカスタム例外クラスを作成してください。
課題2: RAII
ファイルハンドルをRAIIでラップするクラスを作成してください。

次のステップ