見出し画像

C++ 再入門 その38 - 関数に例外を仕込む

前回ライブラリで発生した例外を捕まえる方法について調べました。今回は自分で作成した関数に例外処理を組み込む方法を調べてみましょう。

C++ 再入門 その37 - 例外を捕まえる

C言語と異なりC++では例外が使えるので最初の頃は乱用しがちなのですが、単なるエラーを捕まえるのであれば戻り値をチェックする使い方のほうが見通しもよいですしパフォーマンスにも優れます。例外がその力を発揮するのは例外を関数を超えて投げる場合で、また呼び出し元がエラーを返したのにチェックを怠って致命的な問題を引き起こすことを防ぎたいときも有益です。

そうは言っても多重ループの中でエラーが発生すると、ループから脱出するのが手間取るので goto を使うくらいならと例外を throw して同じ関数の中で catch するという使い方は良くします。この場合、必要であれば処理を「最初から」やり直すようなコードも書けます。※BASICの RESUME(または RESUME NEXT)のように何らかのエラー処理を行ってからエラーが発生した処理を再開するという書き方は出来ません。

まずは難しいことは考えずに文字列(string)を整数に変換する関数を書いてみます。普通は文字コードの性質を利用して範囲チェックの後に式で変換するコードを書くのですが、敢えて文字コードによらない書き方をしてみました。例外を投げる時に例外の型が必要になるのですが、今回はライブラリが用意してくれているruntime_errorを借用しました。

#include <iostream>
#include <string>
#include <stdexcept>
using namespace std;

int str2int(const string& s) {
  int sign = 1;
  int i = 0;
  string::const_iterator p = s.begin();
  if (p == s.end()) return 0;
  if (*p == '-') {
    sign = -1;
    p++;
    if (p == s.end()) return 0;
  }
  while (true) {
    switch (*p) {
      case '0': i += 0; break;
      case '1': i += 1; break;
      case '2': i += 2; break;
      case '3': i += 3; break;
      case '4': i += 4; break;
      case '5': i += 5; break;
      case '6': i += 6; break;
      case '7': i += 7; break;
      case '8': i += 8; break;
      case '9': i += 9; break;
      default:
        throw runtime_error("include illegal charcter.");
    }
    p++;
    if (p == s.end()) break;
    i *= 10;
    if (i < 0) throw runtime_error("overflow.");
  }
  return i * sign;
}

void main() {
  try {
    cout << str2int("-1234567890") << endl;
  }
  catch (const runtime_error& e) {
    cout << "error occured: " << e.what() << endl;
  }
}

-1234567890

実行結果

関数の戻り値が整数型なので戻り値にエラーを表す値を使うことが出来ません。こういう時はグローバル変数を使ってエラーを知らせることもあるのですが、グローバル変数は実はなかなかやっかいですし(特にスレッドがある場合)、チェックを忘れる事態も起こりやすいので、例外が欲しくなるケースです。

符号があるのは先頭の文字だけなので先に判定して最後に反映させています。そうすると関数内で扱う整数は正のみとなるので、これを利用してオーバーフローのチェックをしてみました。サンプルの数字文字列をもう一桁増やすと(処理系の整数のサイズによりますが)確認できるかと思います。

似たような関数は割り算を使う場合の0除算であるとかC++ではあまり裸のポインタを扱いませんが、ポインタが0(NULL)な場合に処理を中断して例外を送出することでプログラムが停止してしまうことを防ぐためにも使えると思います。

今回は例外の型としてありものを使いましたが独自の処理を書くために型も専用のものを使いたくなることも出てくるはずです。そちらについては次回にでも。

ヘッダ画像は、以下のものを使わせていただきました。https://commons.wikimedia.org/wiki/File:ISO_C%2B%2B_Logo.svg
Jeremy Kratz - https://github.com/isocpp/logos , パブリック・ドメイン,
https://commons.wikimedia.org/w/index.php?curid=62851110による

#プログラミング #プログラミング言語 #プログラミング講座 #CPP #例外処理 #exception #stdexcept #runtime_error

いいなと思ったら応援しよう!

kzn 頂いたチップは記事を書くための資料を揃えるために使わせていただきます!