p503〜p510まで

とりあえず残りの例外部分でまとめとく。

標準例外クラス

実は例外クラスは標準でいろいろと用意されているので、それを継承して利用すると吉。
標準例外クラスは最基底クラスにexceptionクラスを持つ。このクラスではプロトタイプ宣言の後にthrow();という指定がなされている。こんな感じ。

virtual const char* what() const throw();

これは例外指定というもので、このカッコ内に例外の型を書いておくとそれ以外の例外が投げられないと言う宣言になる。上の例ではwhat関数は例外を投げないよって言うこと。もし、ここで例外を投げようとした場合は、unexcepcted関数が自動的に呼び出され、デフォルトではterminate関数を呼ぶようになっており、異常終了する。
実際には遅いのと、例外指定が肥大することを恐れて、あまり使われてないらしい。
標準例外クラスはいろいろと種類が用意されているので、例外を利用する場合には一度目を通しておくといいかもしれない。

コンストラクタでの例外

コンストラクタで例外を投げた場合、そのクラスの基底クラスやメンバのデストラクタは通常通り実行されるが、そのクラス自体のコンストラクタは実行されない。つまり、例外が投げられるクラスのコンストラクタ内でメモリ確保などをしてしまうと、リークしてしまう可能性がある。
また、メンバ初期化子で例外が投げられるとそれ以降のメンバのデストラクタは呼ばれない(というか作られてないので呼ばれるわけもない。)
まとめると、次のようになる。

コンストラクタの呼び出しが完了したもののデストラクタは呼ばれ、
コンストラクタの呼び出しが完了していないもののデストラクタは呼ばれない。
p508:中段

初期化子で例外が投げられた場合にもう1点注意することは、必ずコンストラクタの外に例外が投げられることだ。ただ、そのエラー処理についてはそのメンバ変数のデストラクタが行ってくれるのであまり例外をキャッチする必要もない。これについては基底クラスで例外が投げられた場合も同様である。

最後に注意するべきは、例外クラスのコンストラクタが例外を投げてしまう場合である。このとき、例外クラスはまだ構築されていないので、投げられる例外は例外クラスではなく、そのコンストラクタ内で投げられた例外となる。ただ、この挙動は紛らわしいので、例外クラスのコンストラクタは例外を投げるべきではない。(exceptionクラスのコンストラクタには「何も例外を投げない」という例外指定が行われている)

天才プログラマのnyaxtさんとこにこんな話が載っていた。参考までに載せておく。

nyaxtのPC作業ログ-「C++のコンストラクタで例外を投げてはいけない」は迷信
http://d.hatena.ne.jp/nyaxt/20080322#1206155443

デストラクタでの例外

デストラクタでは例外を外に投げないようにする。例えば次のような場合、二重例外と呼ばれる状態になる。

try
{
  Hoge hoge;
  throw exception()
}
catch(...)
{
}

このHogeのデストラクタ内で例外が投げられるとする。throw exception();で例外が投げられると、hogenの寿命は尽きるので、例外を投げている最中に例外が投げられる。この場合、例外が処理されないままteminate関数が呼ばれ、異常終了するこちになっている。これが二重例外。
また、そのほかにもメモリリークなどの問題があるため、デストラクタ内では例外が起こるような操作は行うべきではなく、また、例外が発生する場合はきちんとデストラクタ内で例外を処理するようにする。

初期化子から投げられた例外のキャッチ

関数tryブロックと言う方法を使う。関数tryブロックはコンストラクタ以外にも適用可能。例えばこんな感じ。

Hoge::Hoge()
try:
  m_error()
{
}
catch(const bad_alloc& e)
{
}

このとき、コンストラクタであれば、明示的にthrow;と書かなくても例外が自動的に再送出される。

とりあえず例外終了なので一旦〆。
なんか背中が痛いぞー。眠いぞー。