例外処理: 緊急脱出ボタン
予期しないエラーを適切に処理。RAIIと組み合わせて安全に。
例外
実行フローを中断しエラーを通知する仕組み。
RAII
リソースをオブジェクトのライフタイムに紐付ける。
noexcept
例外を投げないことを保証するキーワード。
例外とは?
例外は「無視できないエラー」を扱うのに適しています。戻り値は無視されることがありますが、例外は誰かがキャッチしなければプログラムが終了します。ただし、頻繁に起きる通常の分岐(例:ファイルがない)には、例外ではなく戻り値(std::optionalなど)を使うのが好ましいです。
#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: 例外を投げないことを保証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でラップするクラスを作成してください。