★例外の基本★


C++では、新しいエラー処理の方法として「例外(Exception)」が用意されています。

どのようなものなのか、例を書きながら説明します。

まずは、これまでの方法で作ったプログラムを作ってみましょう。


従来のエラー処理


プロジェクトを作成し、cppファイルを1つだけ追加してください。

<sample program cpp080-01>

#include <iostream>
#include <fstream>

int main()
{
    std::ifstream ifs;
    
    ifs.open("Data.txt", std::ios::in);

    if (!ifs) {
        std::cout << "Open Error" << std::endl;
        return 1;
    }

    ifs.close();
    
    return 0;
}

<実行結果>

Open Error
続行するには何かキーを押してください・・・

わざとエラーを発生させていますので、「Data.txt」は作らないでください。

存在しないファイルをオープンし、エラーが発生したらメッセージを表示して異常終了しています。


例外


上のプログラムを「例外」を使った方法に変えてみます。

<sample program cpp080-02>

#include <iostream>
#include <fstream>

int main()
{
    std::ifstream ifs;

    try {

        ifs.open("Data.txt", std::ios::in);

        if (!ifs) {
            throw "Open Error";
        }
    }
    catch (const char *pMessage) {
        std::cout << pMessage << std::endl;
        return 1;
    }

    ifs.close();

    return 0;
}

<実行結果>

Open Error
続行するには何かキーを押してください・・・

実行結果は同じですが、かなり書き方が変わっています。

まず、「例外」を扱うには新しい命令を知る必要があります。

try {

    //実行したいプログラム
}
catch(引数) {

    //例外が発生した際に実行するプログラム
}

「try」は「試す」と言う意味ですよね。

「試しに」実行したいプログラムを動かしてみる、というイメージでしょうか。

「catch」は「捕まえる」と言う意味です。

何を「捕まえる」のかと言うと「例外」です。

これを説明するには、もう1つの命令を説明しなければなりません。

throw 例外;

「throw」は「投げる」と言う意味です。

この命令が「例外」を投げ、「catch」で「捕まえる」のです。

「catch」の部分を見ると、

catch (const char *pMessage) {
    std::cout << pMessage << std::endl;
    return 1;
}

「引数」の部分が「char型のポインタ」になっています。

「char型のポインタ」は文字列を受け取る時によく使われます。

「throw」の部分を見ると、

if (!ifs) {
    throw "Open Error";
}

エラーが発生した時に「文字列」を「投げて」います。

投げられた「文字列の例外」を受け取るため「catch」の引数は「char型のポインタ」になっているのです。

もちろん他の型でも大丈夫です。


例外の使い方


これだけだと「前より面倒になったのでは・・・」と言うイメージかも知れません。

では、もう少し発展させてみましょう。

もう1度、これまでのエラー処理でプログラムを書いてみます。

<sample program cpp080-03>

#include <iostream>
#include <fstream>
#include <string>

bool FileOpen(std::ifstream &refInputFileStream, std::string strFilename);

int main()
{
    std::ifstream ifs;

    if (!FileOpen(ifs, "Data.txt")) {
        std::cout << "Open Error" << std::endl;
        return 1;
    }

    ifs.close();

    return 0;
}

bool FileOpen(std::ifstream &refInputFileStream, std::string strFilename)
{
    refInputFileStream.open(strFilename, std::ios::in);

    if (!refInputFileStream) {
        return false;
    }

    return true;
}

<実行結果>

Open Error
続行するには何かキーを押してください・・・

ファイルの読み込みを関数化して、エラーが発生した場合「bool型の戻り値」で分かるようになっています。

これまでも何度か書いてきたようなプログラムですね。

これを例外を使ったプログラムに変えてみます。

<sample program cpp080-04>

#include <iostream>
#include <fstream>
#include <string>

void FileOpen(std::ifstream &refInputFileStream, std::string strFilename);

int main()
{
    std::ifstream ifs;

    try {
        FileOpen(ifs, "Data.txt");
    }
    catch (const char *pMessage) {
        std::cout << pMessage << std::endl;
        return 1;
    }

    ifs.close();

    return 0;
}

void FileOpen(std::ifstream &refInputFileStream, std::string strFilename)
{
    refInputFileStream.open(strFilename, std::ios::in);

    if (!refInputFileStream) {
        throw "Open Error";
    }
}

<実行結果>

Open Error
続行するには何かキーを押してください・・・

関数内でエラーが発生した際に「throw」しています。

main関数の「try」の中には、関数呼び出しが書いてあります。

呼び出した関数内で発生したエラーをmain関数で「捕まえる」事が出来るのです。

これまでは「戻り値で成否を判断」していましたが、戻り値は必要無いため「void」になっています。

このように関数を超えて「例外」を「投げる」事が出来ます。

さらに、プログラムを発展させてみます。

<sample program cpp080-05>

#include <iostream>
#include <fstream>
#include <string>

void ReadData();

void FileOpen(std::ifstream &refInputFileStream, std::string strFilename);

int main()
{
    try {
        ReadData();
    }
    catch (const char *pMessage) {
        std::cout << pMessage << std::endl;
        return 1;
    }

    return 0;
}

void ReadData()
{
    std::ifstream ifs;

    FileOpen(ifs, "Data.txt");
    
    ifs.close();
}

void FileOpen(std::ifstream &refInputFileStream, std::string strFilename)
{
    refInputFileStream.open(strFilename, std::ios::in);

    if (!refInputFileStream) {
        throw "Open Error";
    }
}

<実行結果>

Open Error
続行するには何かキーを押してください・・・

相変わらずmain関数に「try-catch」は書かれています。

「throw」はmain関数から呼ばれたReadData関数が呼んだFileOpen関数に書かれています。

main関数が直接呼び出していない関数でも、「try」の中に書かれた関数が呼び出した関数内で投げられた例外は「捕まえる」事が出来ます。


これを使う事で、戻り値を使う事無くエラー処理が可能になります。

また、エラー処理をまとめる事も出来るため、慣れれば非常に便利な考え方です。

最初は面倒かも知れませんが、色々と使ってみてください。


次へ

戻る

目次へ