C++編【標準ライブラリ】 第17章 例外クラス

このエントリーをはてなブックマークに追加

この章の概要

この章の概要です。

関連する話題が、以下のページにあります。

例外クラス

標準ライブラリには、例外オブジェクトに使うためのクラスが幾つか定義されています。 例外クラス群は、std::exceptionクラスを最上位として、そこから幾つかのクラスが派生する形になっています。 具体的には、以下のような構造です(左が基底クラス側)。

標準の例外クラスの継承図

ただし、規格上は、継承関係にある2つのクラスの間に、他のクラスが割り込むような構造になることは許可されています。

すべての例外クラスが std名前空間に含まれていますが、インクルードする標準ヘッダは同じではありません

基底クラス型を指定して例外を捕捉しようとすれば、その派生クラス型の例外オブジェクトでも捕捉できます(【言語解説】第32章)。 そのため、このように継承構造になっていると都合が良いと言えます。 例外の内容によって、捕捉後の処理を細かく制御したければ、派生クラス型で受け取り、 もう少し緩く、大体同一のカテゴリであれば同じ処理で良ければ、基底クラス型で受け取るという選択が可能です。

また、我々の立場としては、標準ライブラリで定義されている例外クラスから、新たな派生クラスを定義して使うことができます。

exception

std::exceptionクラスは、標準ライブラリの例外クラスの最上位に位置しており、 exception という標準ヘッダで定義されています。

すべての例外がを1つの catch で捕捉したければ、この型を指定すると良いようですが、 実際には、単なる整数値や文字列でも例外オブジェクトとして使用できるため、そういった例外の送出があり得るのなら、 この型で捕捉するだけでは不十分です。

このクラスは、whatメンバ関数を持っており、例外の内容を表す文字列リテラルを返します。 whatメンバ関数が返す文字列の内容は環境依存なので、一定の内容が返されることを期待しないようにして下さい。 通常、次のように使用します。

try {
	// std::exception 派生の何らかの例外が送出される
}
catch (const std::exception& e) {
	std::cerr << e.what() << std::endl;
}

whatメンバ関数は、仮想関数になっているので、 std::exceptionクラスや、その派生クラスを更に継承して独自の例外クラスを定義する場合、 whatメンバ関数をオーバーライドして、内容を書き換えることができます。

bad_alloc

std::bad_allocクラスから作られた例外オブジェクトは、 new演算子が失敗したときに送出されます【言語解説】第14章)。

new演算子を「new(std::nothrow) 型名(コンストラクタに渡す引数)」という形で使用した場合は、 失敗を NULL(C++11以降は nullptr)を返すことで表現し、例外を送出しません。

std::bad_alloc は、new という標準ヘッダで定義されています。

bad_cast

std::bad_castクラスから作られた例外オブジェクトは、 参照に対する dynamic_cast が失敗したときに送出されます【言語解説】第31章)。

std::bad_cast は、typeinfo という標準ヘッダで定義されています。

bad_typeid

std::bad_typeidクラスから作られた例外オブジェクトは、 typeid演算子に、ヌルポインタを渡したときに送出されます【言語解説】第31章)。

std::bad_typeid は、typeinfo という標準ヘッダで定義されています。

bad_exception

std::bad_typeidクラスから作られた例外オブジェクトは、 例外指定で指定していない種類の例外を送出したときに使われることがあります。 例外指定については、【言語解説】第32章を参照して下さい。

std::bad_exception は、exception という標準ヘッダで定義されています。

logic_error

std::logic_errorクラスは、論理的なエラーを表現する基底クラスで、 実際に送出される例外は、(通常は)この派生クラスのオブジェクトになります。

ここから派生する例外オブジェクトは、論理エラーを表現しており、 これは大抵の場合、事前に、引数の値が正常かどうかチェックするなどの方法で回避できる類のエラーです。

例えば、vector の atメンバ関数が範囲外アクセスを検出した場合に送出される out_of_range の場合、 atメンバ関数を呼び出す前に、インデックスが正常範囲内にあるかをチェックすれば回避できます。

std::logic_error および、ここから派生する各クラスは、stdexcept という標準ヘッダで定義されています。

out_of_range

std::out_of_rangeクラスから作られた例外オブジェクトは、 関数が受け取った引数の値が、有効範囲外である場合に使われます。

例えば、vector(第5章)や basic_string(第2章)の atメンバ関数は、 有効範囲外のインデックスを渡されると、この例外を送出します。

invalid_argument

std::invalid_argumentクラスから作られた例外オブジェクトは、 関数が受け取った引数の値が、意味的に不正である場合に使われます。

例えば、bitset(第13章)に、ビットを表現しないような値を渡そうとした場合、 この例外を送出します。

length_error

std::length_errorクラスから作られた例外オブジェクトは、 長さが上限を超えたときに使われます。

例えば、basic_string(第2章)において、文字列の設定や連結などを行った結果、 max_sizeメンバ関数(第2章)で取得できる最大長を超えてしまうような場合に、この例外が送出されます。

domain_error

std::domain_errorクラスは、std::logic_error を基底クラスとしている他のクラス達のどれにも当てはまらない場合を扱います。 ただし、標準規格としては、std::domain_error から作られた例外オブジェクトが送出される場面はありません。

runtime_error

std::runtime_errorクラスは、実行時にしか検出できないエラーを表現する基底クラスで、 実際に送出される例外は、(通常は)この派生クラスのオブジェクトになります。

ここから派生する例外オブジェクトは、実行時エラーを表現しており、 多くの場合、例外を送出させている箇所以外のコードで回避することは難しいです。

例えば、range_error は、何らかの演算を行った結果が、正常な範囲外になった場合に送出されます。 演算を行っている関数の外側で、事前に例外送出が起こるかどうかをチェックすることは、通常は困難です。

std::runtime_error および、ここから派生する各クラスは、stdexcept という標準ヘッダで定義されています。

range_error

std::range_errorクラスから作られた例外オブジェクトは、 何らかの演算を行ったとき、範囲エラーが発生した場合に送出されます。

overflow_error

std::overflow_errorクラスから作られた例外オブジェクトは、 数値演算の際にオーバーフローが発生した場合に送出されます。

underflow_error

std::underflow_errorクラスから作られた例外オブジェクトは、 数値演算の際にアンダーフローが発生した場合に送出されます。

ios_base::failure

std::ios_base::failureクラスから作られた例外オブジェクトは、 入出力ストリームにおけるエラー発生時に送出されます。

std::ios_base::failure は、ios という標準ヘッダで定義されています。

C++11 では、ios_base::failure の基底クラスは、新設された std::system_errorクラスに変更になっています。

C++11 (例外クラスの追加と変更)

C++11 では、例外クラス群に、std::system_errorstd::bad_array_new_lengthstd::future_errorstd::bad_weak_ptrstd::bad_function_callstd::regex_error が追加されました。 また、std::ios_base::failure については、基底クラスが変更になっています。

C++11 における例外クラスの構造は以下のようになっています(左が基底クラス側)。

標準の例外クラスの継承図 (C++11)

std::system_error は、システム(OS) が発生させるエラーを表しています。 std::ios_base::failure は、入出力ストリームにおける失敗を表しており、std::system_error の派生クラスに変更されました。

std::bad_array_new_length は、動的に配列確保を行った際に、要素数の指定が 0以下であったり、 環境が許す最大値を超えていたりした場合に送出されます。 new という標準ヘッダで定義されています。

std::future_error は、std::future や std::promise といった、非同期処理でのエラーが発生したときに送出されます。

std::bad_weak_ptr は、std::weak_ptr から std::shared_ptr を生成しようとしたとき、既にその対象が破棄されていた場合に送出されます。 memory という標準ヘッダで定義されています。

std::bad_function_call は、空の std::functionオブジェクトから呼び出しを行おうとしたときに送出されます。 functional という標準ヘッダで定義されています。

std::regex_error は、regex(正規表現)に関する処理の中でエラーが発生したときに送出されます。 regex という標準ヘッダで定義されています。

これらの変更は、VisualC++ 2013/2015/2017、clang 3.7 のいずれも対応済みです。


練習問題

問題@ ある関数が、std::runtime_error の派生クラスの例外オブジェクトを複数種類、送出する可能性があるとき、 それらを捕捉するコードを書いて下さい。 このとき、どの例外オブジェクトでも同じ処理で良い場合と、それぞれ異なる処理が必要になる場合とを考えてみて下さい。


解答ページはこちら

参考リンク

更新履歴

'2017/3/25 VisualC++ 2017 に対応。

'2016/11/5 新規作成。



前の章へ

次の章へ

C++編のトップページへ

Programming Place Plus のトップページへ