タグ

第16回 安全なプログラム構築のためのエラー処理

はじめに

前回は、コンパイル前に行われる前処理プログラムについて学びました。

これまでの講座では、

  • 変数
  • 条件分岐
  • 繰り返し処理
  • 配列
  • 関数
  • ポインタ
  • 構造体
  • ファイル操作
  • 動的メモリ確保

など、C++でプログラムを作成するための基本的な機能について学んできました。

しかし、実際にプログラムを使用すると、必ずしも作成者が想定した通りに動くとは限りません。

例えば、

  • ユーザーが間違った値を入力する
  • 存在しないファイルを開こうとする
  • 計算できない処理を行う

などの問題が発生する場合があります。

このような問題に対応するために必要なのがエラー処理です。

今回の到達目標

  • エラー処理の必要性を理解する。
  • エラーの種類を理解する。
  • trycatchを使用できる。
  • throwで例外を発生させられる。
  • 安全なプログラムを書く考え方を理解する。

エラー処理とは

エラー処理とは、

プログラムで問題が発生した場合に、適切な対応を行う仕組み

です。

例えば、計算プログラムで、

int result = a / b;

という処理を考えます。

もし、

b = 0;

だった場合、

0で割ることはできないため問題が発生します。

このような場合に、

  • エラーを検出する
  • 利用者へ知らせる
  • プログラムを安全に終了する

といった処理を行う必要があります。

エラーの種類

プログラムで発生するエラーは、大きく3種類に分けることができます。

コンパイルエラー

コンパイルエラーとは、プログラムを実行する前に発見されるエラーです。

例:

int number =

のように、文法が間違っている場合に発生します。

特徴:

  • プログラムを実行できない
  • コンパイラが発見する

実行時エラー

実行時エラーとは、プログラムを実行している途中で発生するエラーです。

例:

  • 0による割り算
  • 存在しないファイルの読み込み
  • 不正な入力

などがあります。

プログラム自体は正しく書かれていても、状況によって発生します。

論理エラー

論理エラーとは、プログラムは動作するものの、結果が間違っているエラーです。

例:

int price = 100; int tax = price * 0.08;

本来は税込価格を求めたいのに、計算方法を間違えている場合などです。

論理エラーはプログラムが停止しないため、発見が難しいエラーです。

エラー処理の基本的な考え方

エラー処理では、

問題が発生する可能性を考えて、事前に対応を用意する

ことが重要です。

例えば、

int age; std::cin >> age;

という入力処理では、

利用者が必ず正しい値を入力するとは限りません。

そのため、

  • 入力値を確認する
  • 問題があれば知らせる
  • 再入力を求める

という処理を追加します。

条件分岐によるエラー処理

最も基本的なエラー処理は、条件分岐を利用する方法です。

例:

#include <iostream> int main() {    int a;    int b;    std::cin >> a;    std::cin >> b;    if(b == 0)    {        std::cout << "0では割れません";    }    else    {        std::cout << a / b;    } }

このように、問題が発生する条件を事前に確認できます。

例外処理とは

C++には、エラーを扱うための仕組みとして例外処理があります。

例外処理では、

  • エラーが発生する可能性がある処理
  • エラーが発生した場合の処理

を分けて記述できます。

基本的な形は次の通りです。

try {    // エラーが発生する可能性がある処理 } catch {    // エラー発生時の処理 }

tryとcatch

tryには、問題が発生する可能性がある処理を書きます。

catchには、問題が発生した場合の処理を書きます。

例:

#include <iostream> int main() {    try    {        int number = 10;        if(number < 0)        {            throw;        }    }    catch(...)    {        std::cout << "エラーが発生しました";    } }

throwによる例外発生

throwを使用すると、意図的に例外を発生させることができます。

例:

#include <iostream> int main() {    int age;    std::cin >> age;    if(age < 0)    {        throw "年齢が不正です";    }    std::cout << age; }

この場合、

年齢が0未満なら例外を発生させます。

発生した例外をcatchする

throwで発生した例外は、catchで受け取ります。

例:

#include <iostream> int main() {    try    {        int age;        std::cin >> age;        if(age < 0)        {            throw "年齢が不正です";        }    }    catch(const char* message)    {        std::cout << message;    } }

実行例:

入力

-5

結果

年齢が不正です

例外クラスを利用する

C++では、標準で用意されている例外クラスを利用することもできます。

例えば、

#include <stdexcept>

を追加すると、

std::runtime_error

などを利用できます。

例:

throw std::runtime_error("エラーです");

このように、エラー内容を持った例外を発生させることができます。

入力エラーへの対応

ユーザー入力では、間違ったデータが入力される可能性があります。

例えば、

int number; std::cin >> number;

で、

abc

と入力すると、整数として読み込めません。

このような場合は、

std::cin.fail()

を利用して入力失敗を確認できます。

例:

#include <iostream> int main() {    int number;    std::cin >> number;    if(std::cin.fail())    {        std::cout << "整数を入力してください";    } }

ファイル操作でのエラー処理

第13回で学んだファイル操作でも、エラー処理は重要です。

例えば、

std::ifstream file("data.txt");

で、存在しないファイルを開こうとした場合があります。

その場合、

if(!file) {    std::cout << "ファイルを開けません"; }

のように確認します。

エラー処理の重要性

安全なプログラムでは、

「エラーが起きないようにする」

だけでは不十分です。

なぜなら、

  • 利用者の入力
  • 外部ファイル
  • ネットワーク通信

など、自分では完全に制御できないものが存在するためです。

そのため、

エラーが発生することを前提として、適切に対応する

ことが重要になります。

まとめ

今回は、安全なプログラムを作成するためのエラー処理について学びました。

  • エラーにはコンパイルエラー、実行時エラー、論理エラーがあります。
  • エラー処理とは、問題発生時に適切な対応を行う仕組みです。
  • 条件分岐を利用してエラーを確認できます。
  • trycatchを使用すると例外処理を行えます。
  • throwを使用すると意図的に例外を発生できます。
  • 実際のプログラムでは、問題が発生することを考えて設計することが重要です。

これで手続き型プログラミング編の基本的な内容は終了です。

次回からは、より大規模なプログラムを管理するための考え方であるオブジェクト指向プログラミングについて学びます。