search
LoginSignup
2
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

Life is Tech ! Tokai Advent Calendar 2020 Day 23

posted at

updated at

いまさら聞けない例外処理入門

本記事はLife is Tech ! Tokai Advent Calendar 2020 23日目の記事になります。

さて、突然ですがみなさん例外処理ってしてますか??

馬鹿にすんな!!当たり前だろ!!!って皆さんはブラウザバックでOKです。

趣味でPythonとか触ってる分には特に意識することもないんですが、コードを書いていればそれなりに必要になる場面があるのでサクッと紹介していきます。(今回はC++を対象に書きますが、どの言語でも構造はそんなに変わらないと思うので自分のメイン言語の例外についても調べてみてください。)

2020/12/26 追記

@yumetodo さんよりいただいたご指摘の内容をもとにプログラムを一部修正しました。

たとえば

まずは以下のような簡単なプログラムを考えます。(プログラム自体に特に意味はありません。)

int hoge(){
    vector<int> array(3);
    array = {1, 2, 3};
    int index = 9;                  // 9番目の要素にアクセスするよ
    int num = array.at(index);         // error : index out of range
    cout << "正常終了" << endl;      // 上の行でエラーが発生するため実行されない(はず)
    return 0;                       // returnもされない(はず)
}

3行目に注目すると、int型の動的配列arrayの中身は、{1, 2, 3}で初期化されています。

つまりarrayの要素数は3つしかないため、要素番号は0~2までしか扱えないはずですね。
しかし、4-5行目で10番目の要素にアクセスしようとしています。要素数が足りないため、ここではout of rangeのエラーが出るでしょう。(コンパイル時の設定によっては出ませんが、初期化していない要素にアクセスできてしまうのが危険であることは理解できるかと思います。)

if文で処理してみる

想定外の挙動をしないようプログラムを書きたい場合、以下のように条件分岐をしてみるのも一つの手かもしれません。

if (index > array.size()) {
    cout << "異常終了" << endl;
    return -1;                    // 異常終了の場合-1をreturnする
} else {
    // やりたい処理をここに書くよ
}
return 0;                         // 正常終了の場合0をreturnする

この場合、戻り値によって処理が正常に終了したか、異常に終了したかを判別することができます。

しかし、上記のようなやり方では通常の処理と例外処理が混在してしまい(明確に例外の処理だけを切り分けられない)、それに付随して、コードの可読性が下がるというような問題点があります。

特に複数人で作業をしていたりするとその影響は顕著で、統一的に例外処理が行われていなくコードの読解が難しくなりがちです。(ただの分岐なのかエラー処理なのか分かりにくかったりする)

また、他にもいろいろありますが

「例外処理 メリット デメリット」

とかで検索するともっと詳しい方々の説明が出てくると思うので今回は割愛します。

try - catchを使ってみよう

例外はtry - catchを用いると明示的に監視できます。
処理自体はtrythrowcatchという3つのキーワードから構成されます。

今日はこの3単語だけ覚えて帰ればOKです。

手順1. tryブロックを記述しよう

まず、例外が発生する可能性のあるコードをtryブロックとして{ }で囲みます

try {
    //例外が発生する可能性のあるコード
}

手順2. 例外をthrowしよう

throwを実行することで、例外を投げることができます。
throwの使い方はreturnと同じような雰囲気であると捉えればよく、

try {
    //例外が発生する可能性のあるコード
    throw exception;
}

のような形になります。exceptionには投げる例外の値を指定します。

throwで例外を投げるとプログラムの実行はtryブロックから即座に抜け出し、投げられた例外に対応したcatchブロックへと移ります。

手順3. catchブロックを記述しよう

tryブロックの中で例外が発生した場合、catchブロックでそれを受け取ります。
catchブロックは例外を受け取るとブロック内に記述されている例外処理を実行するため、ここでは例外を受け取った場合の処理を用意します。

catch (type arg) {
    //例外処理
}

type argには、型と変数名を指定します。
一般的にはポインタの形式で記述して参照を渡すことが多いです。(不要な値のコピーを回避するため)

catchブロックは発生した例外がtype arg部分の型と一致した場合に実行されます。
catchブロックは、tryブロックの直後に記述します。

プログラム例

上記を踏まえて、最初のサンプルプログラムを改良したのが以下のプログラムになります。

int hoge(){
    vector<int> array(3);
    array = {1, 2, 3};
    int index = 9;

    try {
        if (index >= array.size()){
            throw "error : index out of range.\n";
        }
    } catch (char *str) {
        cout << str;
    }

    int num = array.at(index);

    return 0;
}

indexが配列の要素数を超えていた場合の処理をtryブロック内に記述し、分岐内で例外をthrowしています。
誰が見てもこの部分のコードは例外処理だとわかるようになりましたね。

今北産業

  • 例外処理しようね
  • 基本はtry - catchで書くよ
  • throwで例外投げるよ

おわりに

例外処理って必要に駆られないとなかなか触れる機会がないと思うので、これを機に今後の開発で意識されるようになればと思います。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2
Help us understand the problem. What are the problem?