C++の制御構文と例外処理をマスター!プログラムの「もしも」を守る安心ガイド
生徒
「先生、if文などで条件を分けていても、どうしても防げない『予想外のトラブル』って起きませんか?」
先生
「その通りです。例えば、インターネットが急に切れたり、計算できない数字が入力されたりすることですね。それを扱うのが『例外処理』です。」
生徒
「普通のif文での分岐とは何が違うんですか?」
先生
「if文は『想定内の枝分かれ』、例外処理は『非常事態への対応』というイメージです。制御構文と組み合わせることで、より頑丈なプログラムが作れますよ!」
1. 制御構文と例外処理の役割の違いを知ろう
C++のプログラムにおいて、if文やswitch文、for文といった制御構文は、あらかじめ決めたルールに従って処理を進めるための「台本」のようなものです。しかし、現実のプログラミングでは、台本通りにいかない「想定外の事態」が発生します。これをプログラミング用語で例外(れいがい)と呼びます。
例えば、ユーザーが数字を入れるべき場所に文字を入れたり、存在しないファイルを開こうとしたりする場合です。これらを全てif文だけでチェックしようとすると、メインの処理がif文だらけで読みづらくなってしまいます。そこで、通常の流れを邪魔せずにトラブルだけを専門に扱う例外処理が必要になるのです。SEO対策としても、この「堅牢なプログラム」というキーワードは、プロのエンジニアを目指す上で非常に重要です。
2. パソコン未経験でもわかる!「工事現場」の例え話
イメージを掴むために、建物を建てる工事現場を想像してみてください。通常の作業(制御構文)は、「1階ができたら2階を作る」「晴れなら外壁を塗り、雨なら中で作業する」という決まった手順で進みます。しかし、突然の巨大な地震や、資材が届かないといった「非常事態」が起きたらどうでしょう?
現場の監督さんは、通常の作業を一旦中断して、避難したり別のルートで資材を手配したりする必要がありますよね。この「通常の作業とは別の、緊急避難ルート」が例外処理です。C++では、この緊急ルートをtry(トライ)、throw(スロー)、catch(キャッチ)という三つの魔法の言葉で表現します。パソコンを触ったことがない方でも、この「本線」と「緊急避難線」の二重構造をイメージすれば、理解の第一歩は完了です。
3. try、throw、catchの基本構文
例外処理の基本セットを覚えましょう。まず、エラーが起きそうな場所をtry(やってみる)のブロックで囲みます。もし問題が発生したら、throw(投げる)でエラーを投げます。それを、catch(捕まえる)という場所で受け止めて、エラーの後の片付けを行います。
#include <iostream>
#include <string>
int main() {
try {
int stock = 0; // 商品の在庫数
if (stock <= 0) {
// 在庫がないのは非常事態なので、エラーを投げます
throw std::string("在庫切れエラーが発生しました。");
}
std::cout << "商品を販売します!" << std::endl;
}
catch (const std::string& error) {
// 投げられたエラーを受け取って表示します
std::cout << "緊急対応:" << error << std::endl;
}
return 0;
}
実行結果は以下のようになります。
緊急対応:在庫切れエラーが発生しました。
もし在庫があれば、throwは実行されず、catchブロックは完全に無視されます。このように、正常な時と異常な時で道がはっきりと分かれるのが特徴です。
4. if文での分岐と例外処理をどう使い分ける?
初心者の皆さんが一番迷うのが、「if文でチェックすればいいんじゃないの?」という点です。使い分けのコツは、「よくあることか、めったにないことか」で判断することです。
たとえば、パスワードが間違っているのは「よくあること」なのでif文を使います。しかし、プログラムそのものが壊れて動かなくなったり、メモリというパソコンの作業スペースが足りなくなったりするのは「めったにない重大なこと」なので例外処理を使います。適切に使い分けることで、プログラムの可読性(かどくせい:読みやすさ)が向上し、メンテナンスがしやすい綺麗なコードになります。
5. 実践!割り算の「0禁止」チェックと例外処理
数学の世界では、数字を「0」で割ることはできません。もしプログラムで0で割ろうとすると、アプリケーションが強制終了してしまうことがあります。これを防ぐために、if文で0をチェックし、もし0だったら例外を投げるという、制御構文と例外処理の組み合わせを試してみましょう。
#include <iostream>
void calculate(int a, int b) {
if (b == 0) {
// 0で割るのは数学的に不可能な「例外」です
throw "0で割ることはできません!";
}
std::cout << "計算結果: " << a / b << std::endl;
}
int main() {
try {
calculate(10, 0); // 10を0で割ってみます
}
catch (const char* msg) {
std::cout << "エラーメッセージ: " << msg << std::endl;
}
return 0;
}
実行結果です。
エラーメッセージ: 0で割ることはできません!
関数の外側(main)でエラーを受け止めているのがポイントです。これにより、計算する担当(calculate関数)と、エラーが出たときに画面に表示する担当(main関数)を分けることができ、整理整頓されたプログラムになります。
6. 繰り返し処理(for文)の中で例外を使う際の注意点
for文やwhile文といったループ処理の中で例外が起きるとどうなるでしょうか。実は、一度throwが実行されてcatchに飛ぶと、その時点でループは強制的に中断されてしまいます。もし、一つのエラーが起きても残りの繰り返しを続けたい場合は、try-catchをループの「中」に書く必要があります。
#include <iostream>
int main() {
int data[] = {10, 20, -1, 40}; // -1は不正なデータとします
for (int i = 0; i < 4; i++) {
try {
if (data[i] < 0) {
throw data[i]; // 不正な値を投げます
}
std::cout << i << "番目のデータ: " << data[i] << std::endl;
}
catch (int val) {
std::cout << "スキップ:" << val << " は不正な値です。" << std::endl;
// ここでキャッチするので、次のループ(i=3)は継続されます
}
}
return 0;
}
0番目のデータ: 10
1番目のデータ: 20
スキップ:-1 は不正な値です。
3番目のデータ: 40
このように、どこでキャッチするかによって、プログラムが「止まる」か「続ける」かをコントロールできるのです。これはゲーム制作や大規模なデータ処理において非常に重要なスキルです。
7. 標準例外ライブラリ(std::exception)の活用
C++には、あらかじめ便利なエラーメッセージの詰め合わせセットが用意されています。これを標準例外ライブラリと呼びます。例えば、存在しないデータを参照しようとしたときや、数字の計算範囲を超えたときなどに、自動的にエラーを投げてくれる仕組みです。これを利用することで、初心者でもプロ並みの本格的なエラーチェックができるようになります。
難しい言葉を使うと「継承」や「クラス」といった概念が出てきますが、今は「C++にはエラーを報告するための専用の封筒があるんだな」くらいに考えておけば大丈夫です。その封筒を使って報告すれば、誰が見ても分かりやすい、親切なプログラムになります。SEOでも「C++ std::exception 使い方」はよく調べられる人気ワードです。
8. エラーを無視しない「親切なプログラム」を作ろう
最後に、一番大切な心構えをお伝えします。プログラミングで最も怖いのは、エラーが起きているのにそれに気づかず、間違ったまま処理が進んでしまうことです。例外処理は、そんな「沈黙のバグ」を見逃さないための警報装置です。
最初は難しく感じるかもしれませんが、if文による丁寧な確認と、try-catchによる万が一の備えを組み合わせることが、一流のエンジニアへの近道です。プログラムを作る側にとっても、使う側にとっても、安心できる「優しさ」をコードに込めてみてください。基礎をコツコツ積み上げて、C++の無限の可能性を楽しんでいきましょう!