[C++] 例外を投げ捨てるのがあまりよろしくないわけ

前の記事で例外は投げ捨てみたいなことを書いておいてなんだが、投げ捨てるのはあまりよろしくない。

例外を catch しないと std::terminate() が呼び出されて最後は abort(3) されるのだけれども、この場合は RAII が効かず、スタックオブジェクトのデストラクタが呼び出されない。結果としてゴミが残ったりしてしまうことになる。std::set_terminate() でスタックトレースを出力するハンドラをセットして様子を見てみると以下のような出力が得られるはずだ。

$ ./test
Backtrace:
./test(_Z12my_terminatev+0x3b)[0x42249d]
/usr/lib/libstdc++.so.6(+0xcad16)[0x7fd612aa4d16]
/usr/lib/libstdc++.so.6(+0xcad43)[0x7fd612aa4d43]
/usr/lib/libstdc++.so.6(+0xcae3e)[0x7fd612aa4e3e]
./test(_ZN6cybozu4saveERKN4Poco4PathE+0xde)[0x422dd2]
./test(main+0x114)[0x4225f3]
/lib/libc.so.6(__libc_start_main+0xfd)[0x7fd6121dbc4d]
./test[0x4223a9]

cybozu::save() あたりから C++ のランタイムに入って、スタックが巻き戻ることなく my_terminate() が呼び出されているのが分かる。例外処理のランタイムはスタックを検査して対応する catch まで巻き戻るといったことをするわけだが、みつからないときは一切スタックを巻き戻さずにそのまま terminate 処理をするわけだ。

であるからして、やはり C++ で catch( ... ) するイディオムには意味はあるわけですな。

int main() {
    try {
        // do something
    } catch( const std::exception& e ) {
        //
    } catch( ... ) {
        //
    }
}