例外処理
この場合、errnoのようなグローバル変数もしくは別途用意した関数引数経由でエラーコードを返して、呼び出し側で判定する必要がある。たとえばWindows APIのGetFileSize()関数は戻り値でファイルサイズを返すが、混合した設計となっており、エラーが発生した場合は-1を返す。しかし戻り値の型はDWORDつまり符号なし32ビット整数型であるため、実際には0xFFFFFFFFが返却される。これは本来正常値としてありうる値であり、異常系との区別が付かないため、エラーによる結果だったかどうかを判定するにはGetLastError()関数の呼び出しが別途必要になっている。なおGetFileSizeEx()関数は成否を戻り値で、正常系出力を引数で返す設計となっており、GetFileSize()関数の代替として推奨されている。また、C言語において身近な例としてはmallocおよびcalloc、realloc関数があげられる。これらは確保を要求する容量として0を指定したときC言語の規格としてNULLを返して良いと定義されている。このためこれらの関数の戻り値では記憶空間の容量不足か引数に0を指定したか判断できず切り分けのためにerrnoを調べるか引数を調べる必要がある。C++において同等のnew演算子ではこの点を容量不足のときだけ例外を投げることによって改善している。
言語レベルでの例外処理はこれらの欠点を解消し、例外を確実に、かつ統一的に処理する目的で導入されたものと言える。
1 2 “第 5 章 例外処理 (C++ プログラミングガイド)”. docs.oracle.com. 2019年10月26日閲覧。
1 2 “IPA ISEC セキュア・プログラミング講座:C/C++言語編 第6章 フェイルセーフ:体系だてたエラーハンドリング”. www.ipa.go.jp. 2019年10月26日閲覧。
1 2 “エラー処理をパターンにはめよう”. Codezine. 2019年10月26日閲覧。
↑ Bjarne Stroustrup. “Appendix E: Standard-Library Exception Safety in "The C++ Programming Language" (3rd Edition).Addison-Wesley, ISBN 0-201-88954-4”. 2013年5月1日閲覧。
↑ “Exception-Safety in Generic Components”. 2013年5月1日閲覧。
↑ http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1997/N1077.asc
↑ /EH (例外処理モデル)
↑ ただし、例外処理中にもう一度別のデストラクタから例外が発生してしまうと復帰できなくなるため、デストラクタから例外を発生させるべきではないとされる。
↑ 例外指定は、 C++関数によって伝達される例外の種類についてプログラマが意図したものを示す言語機能です。 Microsoft Docs Visual C++
↑ C++17ではこの動的例外仕様が削除される。C++日本語リファレンス
↑ class StopIteration Ruby 1.9.3 リファレンスマニュアル(2013年10月7日閲覧)。
↑ 組み込み例外 Python 2.7ドキュメンテーション(2013年10月7日閲覧)。
↑ module function Kernel.#throw Ruby 1.9.2 リファレンスマニュアル(2013年10月7日閲覧)。
↑ たとえばCOMではメソッドの戻り値として、MAKE_HRESULT()マクロを用いてHRESULTコードを定義するが、異常系は負数となる。
↑ CUDAのように列挙型で定義した正の数をエラーコードとして使用するライブラリもある。CUDA Runtime API :: CUDA Toolkit Documentation
↑ GetFileSize 関数
↑ GetFileSize function (Windows)
↑ “C言語規格のドラフト”. 2018年11月21日閲覧。
割り当てられていない記憶領域へのアクセス 不正な値が与えられたポインタで参照する、或いは機械語レベルで不正な値が与えられたインデックスレジスタ等を用いてメモリ参照することとなった場合 ページフォールト
プログラミング言語において、何も参照していないハンドルやポインター(Nullポインタ)を参照して操作しようとした場合(例としてJavaにおけるNullPointerExceptionなど)。
注意点として、あらゆる例外が抽象レベルに依存せずすべて異常系であるとは限らない。例えばページフォルトはカーネル内部のメモリなど例外が許されない環境下ではエラーとなるが、仮想記憶を採用したOSにおけるユーザプロセスのメモリは常時物理的に存在するとは限らないためページフォルトを正常系として処理する必要がある。また、例外処理中にさらなる例外が発生した場合は、通常なら正常系となる事象が異常系に変わる場合がある。詳しくは#例外のネストを参照されたい。
また、Microsoft Visual C++といった一部の処理系では、コンパイラオプションの指定によりC++例外だけでなくOSが投げた構造化例外も省略子を用いたcatchで捕捉できる。catch文の中では(7)のように引数の無いthrowを用いた場合、例外の再送を意味する。省略子を用いたcatchの場合は例外情報を判断できないため必須であるが、省略子を使わないcatchでも派生型の例外を基底型で受け取ってしまった場合のスライシングを防ぐために必要となる。
tryブロックは(8)のように関数全体に適用することもできる。これをfunction-try-blockと言う。catch文では局所変数を参照できず引数だけしか参照できないが、コンストラクタの初期化リストで発生した例外やデストラクタから投げられた例外はこの書き方でしか捕捉することができない。
例外処理構文を最初に実装したのはAdaであるが、C++の例外処理構文はJavaやJavaScript、C#など多くの後発言語の規範となった。