p476〜p502まで

やっとこさ折り返し。うーん、少しペースが遅いか・・・。

goto文

goto文は初期化を伴う変数宣言を飛び越えることができない。
ラベルの後には必ず何らかの文が必要。
p480

ここだけ抑えとけばいいかな。あとは理解してるはず。使わんけど。

例外

うーんうーん、きりが悪い。次は例外なんだけれどもこれはまとめて書いておきたい。というか、いまさらになってタイトルのつけ方がいやすぎ。まぁあとで見直すときはカテゴリからみれば結構合わせて読みやすいか・・・。とりあえず追記する形でやっていこう。

例外処理の基本形
try
{
  if(エラー判定)
  {
    throw エラーメッセージ;
  }
}
catch(エラーメッセージと同型の変数宣言)
{
  エラー処理
}

C++での基本的な例外の投げ方は上のとおり。throw式で投げられたあとの処理は実行されず、catch節に飛ぶ。また、catch節は正常時は実行されない。

スタック巻き戻し

例外処理は関数外にも届く。例えばこんな感じ。

void Hoge()
{
  throw "hogeった!";
}

int main()
{
  try
  {
     Hoge();
  }
  catch(const char* error)
 {
     cerr << error << endl;
     return EXIT_FAILURE;
  }
}

関数内でthrowが投げられるとtryブロックが見つかるまでどんどん関数を抜けていく。このとき、tryブロックがあっても、投げられた値の方に対応するcatch節がなければ関数を抜け続ける。これをスタック巻き戻し(stack unwinding)と呼ぶ。
もし、最後までtryブロックがなければteminateという関数が自動的に呼ばれ、プログラムが以上終了する。
なお、関数を抜けていく際には通常自動変数のデストラクタはきちんと呼ばれるが、最後まで例外がキャッチされなかった場合は呼ばれない可能性がある。

複数のcatch節

catch節は続けて複数書くことができる。この場合、一番初めに例外を受け取ったcatch節のみ実行される。

try
{
  // do something...
}
catch(int hoge)
{
  // error
}
catch(char* hoge)
{
  // error
}

また、catch(...)と書けばあらゆる例外をキャッチすることができるが、想定外の例外すらもキャッチしてしまうため、きちんと考えて使うべし。

catch節内でのthrow

catch節内でthrowを投げた場合、その外にあるtry節でキャッチされる。例えばこんな感じ。

void Hoge()
{
  try
  {
    throw 1;
  }
  catch(int error)
  {
    throw "error";
  }
  catch(const char* error)
  {
    // ここにはこない
  }
}

int main()
{
  try
  {
    Hoge();
  }
  catch(const char* error)
  {
    // Hoge内のcatch節内のthrowはここでcatchされる
  }
}

なので、エラー処理を外に出したくない場合はtry-catchでそのthrowを囲む必要がでてくる。

例外の再送出

catch節でcatchした例外を外にも通知したいときは次にようにする。

void Hoge()
{
  try
  {
    throw 1;
  }
  catch(...)
  {
    throw;  // これだけ
  }
}

int main()
{
  try
  {
    Hoge();
  }
  catch(int e)
  {
    // do something...
  }
}

これを例外の再送出(exception rethrow)という。ただ、想定していなかった例外に対応するためには、基本的にはデストラクタを実行しさえすれば修了処理が完了するようにしておく。

クラスを利用した例外処理

throwで投げる例外はクラスも指定できる。クラスを指定する場合は通常次のようにする。

throw クラス名(コンストラクタの引数);
// または
クラス名 e();
throw e;
// で、catch節はこんな感じでconst参照でとる。
catch(const クラス名& e)
{
}

ここで参照で受け取っているオブジェクトのアドレスはthrowで投げられたクラスの一時オブジェクトのアドレスとなる。
また、クラスを利用して例外を投げる際、catch節でキャッチするクラスではアップキャストが可能なので、同じエラー処理としてまとめたい場合にはそれを利用する。アップキャストで例外を受ける場合には必ず参照を利用しなければならない。ただし、アップキャストを利用する場合、catch節の順番に気をつけること。派生クラスで受けたい場合が、先に基底クラスで受ける旨を記述し、そちらに例外がわたってしまうかもしれない。

例外を利用する場合に心得

どの例外が発生して、どの例外であればエラーから復帰が可能で、
どの例外はキャッチしないでおくか、と言ったことを考えながら処理しよう。
p502:中段

例外はエラー処理意外には使わない
(遅いのと、try-throw-catchを見たら普通はエラー処理と思うため。)
p502:コラム

とりあえず基本的な部分はここまで。明日、もうちょっと補足を入れて例外は終了。