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」の中に書かれた関数が呼び出した関数内で投げられた例外は「捕まえる」事が出来ます。
これを使う事で、戻り値を使う事無くエラー処理が可能になります。
また、エラー処理をまとめる事も出来るため、慣れれば非常に便利な考え方です。
最初は面倒かも知れませんが、色々と使ってみてください。