C++でのプログラミングにおいて、エラーハンドリングは信頼性の高いコードを作成するために不可欠な要素です。本記事では、条件分岐と例外処理を組み合わせてエラーハンドリングを行う方法について、基本から応用まで詳しく解説します。これにより、エラーに強い堅牢なプログラムを作成するための知識を深めることができます。
エラーハンドリングの基本概念
エラーハンドリングは、プログラムが実行中に発生する予期しない状況を適切に処理し、プログラムの異常終了や予期しない動作を防ぐための重要な技術です。C++では、エラーを検出し、適切な対策を講じるためのメカニズムがいくつか用意されています。このセクションでは、エラーハンドリングの基本概念と、その重要性について説明します。
エラーハンドリングの重要性
エラーが発生した場合に適切に対応することで、システムの信頼性を向上させ、ユーザーに良好な体験を提供することができます。エラーを無視したり、適切に処理しないと、プログラムの予期しない動作やクラッシュを引き起こす可能性があります。
エラーハンドリングの方法
エラーハンドリングには主に二つの方法があります。
- 条件分岐(if文、switch文):条件を評価してエラーの有無を確認し、必要な処理を行います。
- 例外処理(try-catchブロック):エラーが発生したときに例外を投げ、適切な場所で捕捉して処理します。
これらの方法を組み合わせることで、より堅牢なエラーハンドリングを実現できます。
条件分岐によるエラーハンドリング
条件分岐を用いたエラーハンドリングは、エラーが発生する可能性がある状況を前もって確認し、適切な処理を行う方法です。C++では、主にif文やswitch文を使用してエラーチェックを行います。
if文を用いたエラーハンドリング
if文を使って条件を評価し、エラーが発生した場合に特定の処理を行う方法を紹介します。
#include <iostream>
#include <fstream>
void readFile(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Error: Unable to open file " << filename << std::endl;
return;
}
// ファイルの読み取り処理
file.close();
}
int main() {
readFile("example.txt");
return 0;
}
この例では、ファイルを開く際にエラーが発生した場合、エラーメッセージを表示し、処理を中断します。
switch文を用いたエラーハンドリング
switch文を使用して、複数のエラー条件を効率的に処理する方法を紹介します。
#include <iostream>
void processErrorCode(int errorCode) {
switch (errorCode) {
case 0:
std::cout << "No error." << std::endl;
break;
case 1:
std::cerr << "Error: Invalid input." << std::endl;
break;
case 2:
std::cerr << "Error: File not found." << std::endl;
break;
case 3:
std::cerr << "Error: Access denied." << std::endl;
break;
default:
std::cerr << "Error: Unknown error code." << std::endl;
break;
}
}
int main() {
int errorCode = 2; // 例としてエラーコード2を設定
processErrorCode(errorCode);
return 0;
}
この例では、エラーコードに応じて異なるエラーメッセージを表示します。switch文を使うことで、複数の条件を整理して処理することができます。
条件分岐を使ったエラーハンドリングは、シンプルで分かりやすい方法ですが、複雑なエラー処理が必要な場合には、次に紹介する例外処理の利用が適しています。
例外処理の基本
例外処理は、エラーが発生したときに通常のプログラムの流れを中断し、エラーハンドリングのための特別なコードブロックに制御を渡す方法です。C++では、try-catchブロックを使用して例外を捕捉し、適切に処理します。
try-catchブロックの基本構造
例外処理の基本的な構造は次のようになります。
#include <iostream>
void divide(int a, int b) {
try {
if (b == 0) {
throw std::runtime_error("Division by zero!");
}
std::cout << "Result: " << a / b << std::endl;
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
}
int main() {
divide(10, 0);
return 0;
}
この例では、b
が0の場合にstd::runtime_error
例外を投げ、それをキャッチしてエラーメッセージを表示しています。
標準例外クラスの使用
C++標準ライブラリには、多くの標準例外クラスが用意されています。これらを利用することで、一般的なエラーを簡単に処理できます。
#include <iostream>
#include <stdexcept>
void checkAge(int age) {
try {
if (age < 0) {
throw std::invalid_argument("Age cannot be negative.");
}
std::cout << "Valid age: " << age << std::endl;
} catch (const std::invalid_argument& e) {
std::cerr << "Invalid argument: " << e.what() << std::endl;
}
}
int main() {
checkAge(-5);
return 0;
}
この例では、年齢が負の値の場合にstd::invalid_argument
例外を投げ、キャッチしてエラーメッセージを表示しています。
例外の再スロー
場合によっては、捕捉した例外を再度スローして、呼び出し元でさらに処理を行うことが必要になる場合があります。
#include <iostream>
#include <stdexcept>
void innerFunction() {
try {
throw std::runtime_error("Inner function error.");
} catch (const std::runtime_error& e) {
std::cerr << "Caught in innerFunction: " << e.what() << std::endl;
throw; // 再スロー
}
}
void outerFunction() {
try {
innerFunction();
} catch (const std::runtime_error& e) {
std::cerr << "Caught in outerFunction: " << e.what() << std::endl;
}
}
int main() {
outerFunction();
return 0;
}
この例では、innerFunction
内で捕捉した例外を再スローし、outerFunction
で再度捕捉しています。
例外処理を適切に活用することで、エラーハンドリングがより柔軟かつ強力になります。次のセクションでは、独自の例外クラスを作成し、特定のエラーに対するカスタム処理を行う方法を解説します。
例外クラスのカスタマイズ
独自の例外クラスを作成することで、特定のエラー条件に応じたカスタム処理を行うことができます。C++では、標準ライブラリの例外クラスを継承して独自の例外クラスを作成することが可能です。
カスタム例外クラスの作成
独自の例外クラスを作成する際には、通常std::exception
クラスを継承します。以下は、カスタム例外クラスの基本的な作成方法です。
#include <iostream>
#include <exception>
// カスタム例外クラス
class MyException : public std::exception {
private:
std::string message;
public:
MyException(const std::string& msg) : message(msg) {}
virtual const char* what() const noexcept override {
return message.c_str();
}
};
void throwCustomException() {
throw MyException("This is a custom exception!");
}
int main() {
try {
throwCustomException();
} catch (const MyException& e) {
std::cerr << "Caught custom exception: " << e.what() << std::endl;
}
return 0;
}
この例では、MyException
クラスを作成し、エラーメッセージをカスタマイズしています。what
メソッドをオーバーライドしてエラーメッセージを返します。
カスタム例外クラスの使用例
次に、実際の使用例として、ファイル操作におけるカスタム例外クラスを紹介します。
#include <iostream>
#include <fstream>
#include <exception>
// ファイル操作の例外クラス
class FileException : public std::exception {
private:
std::string message;
public:
FileException(const std::string& msg) : message(msg) {}
virtual const char* what() const noexcept override {
return message.c_str();
}
};
void readFile(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
throw FileException("Unable to open file: " + filename);
}
// ファイルの読み取り処理
file.close();
}
int main() {
try {
readFile("nonexistent_file.txt");
} catch (const FileException& e) {
std::cerr << "Caught file exception: " << e.what() << std::endl;
}
return 0;
}
この例では、FileException
クラスを作成し、ファイルを開けなかった場合に例外を投げています。main
関数内でこの例外をキャッチし、エラーメッセージを表示します。
複数のカスタム例外クラス
複数のカスタム例外クラスを作成することで、エラーの種類ごとに異なる処理を行うことができます。
#include <iostream>
#include <exception>
// 基底クラス
class MyBaseException : public std::exception {
private:
std::string message;
public:
MyBaseException(const std::string& msg) : message(msg) {}
virtual const char* what() const noexcept override {
return message.c_str();
}
};
// 派生クラス1
class InvalidInputException : public MyBaseException {
public:
InvalidInputException(const std::string& msg) : MyBaseException(msg) {}
};
// 派生クラス2
class CalculationException : public MyBaseException {
public:
CalculationException(const std::string& msg) : MyBaseException(msg) {}
};
void processInput(int input) {
if (input < 0) {
throw InvalidInputException("Input cannot be negative!");
} else if (input == 0) {
throw CalculationException("Division by zero!");
}
std::cout << "Processing input: " << input << std::endl;
}
int main() {
try {
processInput(-1);
} catch (const InvalidInputException& e) {
std::cerr << "Caught invalid input exception: " << e.what() << std::endl;
} catch (const CalculationException& e) {
std::cerr << "Caught calculation exception: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Caught generic exception: " << e.what() << std::endl;
}
return 0;
}
この例では、MyBaseException
を基底クラスとし、InvalidInputException
とCalculationException
を派生クラスとして作成しています。エラーの種類に応じて異なる例外を投げ、キャッチして適切なメッセージを表示しています。
カスタム例外クラスを使うことで、エラーハンドリングの柔軟性が向上し、特定のエラーに対する詳細な情報を提供できます。次のセクションでは、条件分岐と例外処理を組み合わせてエラーハンドリングを強化する方法を紹介します。
条件分岐と例外処理の併用
条件分岐と例外処理を組み合わせることで、エラーハンドリングの柔軟性と堅牢性を高めることができます。このセクションでは、両者を効果的に組み合わせる方法を紹介します。
基本的な併用例
条件分岐で事前にエラーをチェックし、重大なエラーが発生した場合に例外を投げる方法を紹介します。
#include <iostream>
#include <stdexcept>
void processValue(int value) {
if (value < 0) {
std::cerr << "Warning: Negative value encountered." << std::endl;
return; // 条件分岐による処理
} else if (value == 0) {
throw std::runtime_error("Error: Division by zero!"); // 例外処理による重大なエラーの処理
}
std::cout << "Processing value: " << value << std::endl;
}
int main() {
try {
processValue(-1); // 警告が出るが例外は投げられない
processValue(0); // 例外が投げられる
} catch (const std::runtime_error& e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
}
return 0;
}
この例では、負の値の場合は警告を出し、0の場合は例外を投げています。条件分岐と例外処理を併用することで、エラーの重要度に応じた適切な処理が可能になります。
ファイル操作の併用例
ファイル操作の際に、条件分岐と例外処理を併用してエラーハンドリングを行う方法を紹介します。
#include <iostream>
#include <fstream>
#include <stdexcept>
void readFile(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Warning: Unable to open file " << filename << std::endl;
return;
}
try {
std::string line;
while (std::getline(file, line)) {
if (line.empty()) {
throw std::runtime_error("Error: Empty line detected!"); // 重大なエラーの場合に例外を投げる
}
std::cout << line << std::endl;
}
} catch (const std::runtime_error& e) {
std::cerr << "Caught exception while reading file: " << e.what() << std::endl;
}
file.close();
}
int main() {
readFile("example.txt");
return 0;
}
この例では、ファイルが開けない場合は警告を出し、ファイルの読み取り中に重大なエラーが発生した場合に例外を投げています。
複数の例外を処理する方法
条件分岐と例外処理を組み合わせて、複数の異なるエラーを処理する方法を紹介します。
#include <iostream>
#include <stdexcept>
void validateInput(int input) {
if (input < 0) {
std::cerr << "Warning: Negative input received." << std::endl;
return;
} else if (input == 0) {
throw std::invalid_argument("Input cannot be zero.");
} else if (input > 100) {
throw std::out_of_range("Input exceeds maximum value of 100.");
}
std::cout << "Valid input: " << input << std::endl;
}
int main() {
try {
validateInput(-1); // 警告が出るが例外は投げられない
validateInput(0); // std::invalid_argument例外が投げられる
validateInput(101); // std::out_of_range例外が投げられる
} catch (const std::invalid_argument& e) {
std::cerr << "Invalid argument: " << e.what() << std::endl;
} catch (const std::out_of_range& e) {
std::cerr << "Out of range: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "General exception: " << e.what() << std::endl;
}
return 0;
}
この例では、入力値に応じて異なる例外を投げ、catchブロックでそれぞれの例外を個別に処理しています。
条件分岐と例外処理を効果的に組み合わせることで、エラーハンドリングの精度と柔軟性を向上させることができます。次のセクションでは、標準ライブラリを用いたエラーハンドリングの実例を紹介します。
標準ライブラリを用いたエラーハンドリング
C++標準ライブラリには、多くのエラーハンドリングをサポートする機能が含まれています。これらの機能を利用することで、エラーハンドリングを簡単かつ効果的に行うことができます。
標準例外クラスの活用
標準ライブラリには、std::exception
をはじめとする多くの例外クラスが用意されています。これらを利用することで、エラーハンドリングを統一的に行うことができます。
#include <iostream>
#include <stdexcept>
void processString(const std::string& str) {
if (str.empty()) {
throw std::invalid_argument("String cannot be empty.");
}
std::cout << "Processing string: " << str << std::endl;
}
int main() {
try {
processString(""); // std::invalid_argument例外が投げられる
} catch (const std::invalid_argument& e) {
std::cerr << "Invalid argument: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "General exception: " << e.what() << std::endl;
}
return 0;
}
この例では、文字列が空の場合にstd::invalid_argument
例外を投げ、catchブロックでエラーメッセージを表示します。
スマートポインタを用いたリソース管理
C++11以降では、スマートポインタ(std::unique_ptr
やstd::shared_ptr
)を利用することで、メモリ管理におけるエラーを防ぐことができます。
#include <iostream>
#include <memory>
#include <stdexcept>
void useResource() {
std::unique_ptr<int> ptr(new int(10));
if (!ptr) {
throw std::runtime_error("Failed to allocate memory.");
}
std::cout << "Resource value: " << *ptr << std::endl;
}
int main() {
try {
useResource(); // メモリ割り当てが成功し、例外は投げられない
} catch (const std::runtime_error& e) {
std::cerr << "Runtime error: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "General exception: " << e.what() << std::endl;
}
return 0;
}
この例では、std::unique_ptr
を使用してメモリを管理し、メモリ割り当てが失敗した場合に例外を投げています。スマートポインタを使用することで、メモリリークを防ぎ、コードの信頼性を向上させることができます。
ファイル操作における標準ライブラリの活用
ファイル操作の際に標準ライブラリを利用してエラーハンドリングを行う方法を紹介します。
#include <iostream>
#include <fstream>
#include <stdexcept>
void readFile(const std::string& filename) {
std::ifstream file(filename);
if (!file) {
throw std::ios_base::failure("Failed to open file: " + filename);
}
std::string line;
while (std::getline(file, line)) {
if (file.bad()) {
throw std::ios_base::failure("Error reading from file: " + filename);
}
std::cout << line << std::endl;
}
file.close();
}
int main() {
try {
readFile("example.txt");
} catch (const std::ios_base::failure& e) {
std::cerr << "File I/O error: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "General exception: " << e.what() << std::endl;
}
return 0;
}
この例では、std::ifstream
を使用してファイルを読み込み、ファイルが開けない場合や読み込み中にエラーが発生した場合に例外を投げています。
標準ライブラリを利用することで、コードの再利用性と保守性が向上し、エラーハンドリングがより簡単になります。次のセクションでは、よくあるエラーパターンとその対策について解説します。
よくあるエラーパターンと対策
C++プログラムでは、特定のエラーパターンが頻繁に発生します。これらのエラーパターンを理解し、適切に対策を講じることで、プログラムの信頼性と安定性を向上させることができます。このセクションでは、よくあるエラーパターンとその対策を紹介します。
NULLポインタ参照
NULLポインタを参照することは、プログラムのクラッシュや予期しない動作を引き起こす一般的なエラーです。NULLポインタ参照を防ぐための対策を紹介します。
#include <iostream>
void processPointer(int* ptr) {
if (ptr == nullptr) {
std::cerr << "Error: NULL pointer reference." << std::endl;
return;
}
std::cout << "Pointer value: " << *ptr << std::endl;
}
int main() {
int* ptr = nullptr;
processPointer(ptr); // エラーメッセージが表示される
return 0;
}
この例では、ポインタがNULLであるかをチェックし、NULLの場合にエラーメッセージを表示しています。
バッファオーバーフロー
バッファオーバーフローは、バッファの範囲を超えてデータを書き込むことによって発生するエラーです。これを防ぐためには、データの長さを事前にチェックすることが重要です。
#include <iostream>
#include <cstring>
void copyString(char* dest, const char* src, size_t destSize) {
if (strlen(src) >= destSize) {
std::cerr << "Error: Buffer overflow detected." << std::endl;
return;
}
std::strcpy(dest, src);
std::cout << "Copied string: " << dest << std::endl;
}
int main() {
char buffer[10];
copyString(buffer, "This is a test string.", sizeof(buffer)); // エラーメッセージが表示される
return 0;
}
この例では、コピー先のバッファのサイズを事前にチェックし、バッファオーバーフローが発生しないようにしています。
リソースリーク
リソースリークは、メモリやファイルハンドルなどのリソースを適切に解放しないことで発生するエラーです。リソースリークを防ぐためには、リソースを適切に管理することが重要です。
#include <iostream>
#include <fstream>
void processFile(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Error: Unable to open file " << filename << std::endl;
return;
}
// ファイルの処理
file.close(); // ファイルを適切に閉じる
}
int main() {
processFile("example.txt");
return 0;
}
この例では、ファイルを使用した後に適切に閉じることで、リソースリークを防いでいます。
インデックスの範囲外アクセス
配列やベクターのインデックスが範囲外になると、プログラムのクラッシュや不正な動作を引き起こします。これを防ぐためには、インデックスの範囲を事前にチェックすることが重要です。
#include <iostream>
#include <vector>
void accessVectorElement(const std::vector<int>& vec, size_t index) {
if (index >= vec.size()) {
std::cerr << "Error: Index out of range." << std::endl;
return;
}
std::cout << "Element at index " << index << ": " << vec[index] << std::endl;
}
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
accessVectorElement(vec, 10); // エラーメッセージが表示される
return 0;
}
この例では、インデックスが範囲外であるかをチェックし、範囲外の場合にエラーメッセージを表示しています。
演算エラー
ゼロによる除算やオーバーフローなどの演算エラーは、プログラムのクラッシュや不正な動作を引き起こします。これを防ぐためには、演算前に条件をチェックすることが重要です。
#include <iostream>
void divide(int a, int b) {
if (b == 0) {
std::cerr << "Error: Division by zero." << std::endl;
return;
}
std::cout << "Result: " << a / b << std::endl;
}
int main() {
divide(10, 0); // エラーメッセージが表示される
return 0;
}
この例では、除算前にゼロでないかをチェックし、ゼロの場合にエラーメッセージを表示しています。
これらのエラーパターンと対策を理解することで、C++プログラムの信頼性と安定性を大幅に向上させることができます。次のセクションでは、ファイル操作における具体的なエラーハンドリングの実装例を紹介します。
応用例:ファイル操作におけるエラーハンドリング
ファイル操作は多くのプログラムで重要な役割を果たしますが、エラーが発生しやすい箇所でもあります。このセクションでは、ファイル操作における具体的なエラーハンドリングの実装例を紹介します。
ファイルを開く際のエラーハンドリング
ファイルを開く際には、ファイルが存在しない場合やアクセス権がない場合にエラーが発生します。このエラーを適切に処理する方法を紹介します。
#include <iostream>
#include <fstream>
#include <stdexcept>
void openFile(const std::string& filename) {
std::ifstream file(filename);
if (!file) {
throw std::ios_base::failure("Failed to open file: " + filename);
}
std::cout << "File opened successfully: " << filename << std::endl;
file.close();
}
int main() {
try {
openFile("example.txt"); // 存在しないファイル
} catch (const std::ios_base::failure& e) {
std::cerr << "File I/O error: " << e.what() << std::endl;
}
return 0;
}
この例では、ファイルが開けない場合に例外を投げ、catchブロックでエラーメッセージを表示します。
ファイル読み込み時のエラーハンドリング
ファイルを読み込む際に発生する可能性のあるエラーを処理する方法を紹介します。
#include <iostream>
#include <fstream>
#include <stdexcept>
void readFile(const std::string& filename) {
std::ifstream file(filename);
if (!file) {
throw std::ios_base::failure("Failed to open file: " + filename);
}
std::string line;
while (std::getline(file, line)) {
if (file.bad()) {
throw std::ios_base::failure("Error reading from file: " + filename);
}
std::cout << line << std::endl;
}
file.close();
}
int main() {
try {
readFile("example.txt");
} catch (const std::ios_base::failure& e) {
std::cerr << "File I/O error: " << e.what() << std::endl;
}
return 0;
}
この例では、ファイルの読み込み中にエラーが発生した場合に例外を投げています。catchブロックでこれらのエラーを適切に処理します。
ファイル書き込み時のエラーハンドリング
ファイルにデータを書き込む際のエラーを処理する方法を紹介します。
#include <iostream>
#include <fstream>
#include <stdexcept>
void writeFile(const std::string& filename, const std::string& data) {
std::ofstream file(filename);
if (!file) {
throw std::ios_base::failure("Failed to open file for writing: " + filename);
}
file << data;
if (!file) {
throw std::ios_base::failure("Error writing to file: " + filename);
}
file.close();
}
int main() {
try {
writeFile("output.txt", "Hello, world!");
} catch (const std::ios_base::failure& e) {
std::cerr << "File I/O error: " << e.what() << std::endl;
}
return 0;
}
この例では、ファイルの書き込み中にエラーが発生した場合に例外を投げています。catchブロックでこれらのエラーを適切に処理します。
ファイル操作の統合的なエラーハンドリング
ファイルの開閉、読み込み、書き込みのすべてに対するエラーハンドリングを統合的に行う方法を紹介します。
#include <iostream>
#include <fstream>
#include <stdexcept>
void processFile(const std::string& inputFilename, const std::string& outputFilename) {
std::ifstream inputFile(inputFilename);
if (!inputFile) {
throw std::ios_base::failure("Failed to open input file: " + inputFilename);
}
std::ofstream outputFile(outputFilename);
if (!outputFile) {
throw std::ios_base::failure("Failed to open output file: " + outputFilename);
}
std::string line;
while (std::getline(inputFile, line)) {
if (inputFile.bad()) {
throw std::ios_base::failure("Error reading from file: " + inputFilename);
}
outputFile << line << std::endl;
if (outputFile.bad()) {
throw std::ios_base::failure("Error writing to file: " + outputFilename);
}
}
inputFile.close();
outputFile.close();
}
int main() {
try {
processFile("input.txt", "output.txt");
} catch (const std::ios_base::failure& e) {
std::cerr << "File I/O error: " << e.what() << std::endl;
}
return 0;
}
この例では、ファイルの開閉、読み込み、書き込みのすべてに対してエラーハンドリングを行い、各操作で発生する可能性のあるエラーを適切に処理しています。
ファイル操作におけるエラーハンドリングを適切に行うことで、プログラムの信頼性と安定性を向上させることができます。次のセクションでは、学習内容を確認するための演習問題を提供します。
演習問題
これまでに学んだ内容を確認するために、いくつかの演習問題を提供します。これらの問題を解くことで、C++における条件分岐と例外処理を用いたエラーハンドリングの理解を深めることができます。
演習問題1: ファイルの存在チェック
指定されたファイルが存在するかどうかを確認するプログラムを作成してください。ファイルが存在しない場合は、適切なエラーメッセージを表示してください。
#include <iostream>
#include <fstream>
bool fileExists(const std::string& filename) {
std::ifstream file(filename);
return file.is_open();
}
int main() {
std::string filename = "example.txt";
if (fileExists(filename)) {
std::cout << "File exists: " << filename << std::endl;
} else {
std::cerr << "Error: File not found: " << filename << std::endl;
}
return 0;
}
演習問題2: 入力の検証と例外処理
ユーザーから入力された数値が負の値であれば例外を投げ、例外を捕捉して適切なメッセージを表示するプログラムを作成してください。
#include <iostream>
#include <stdexcept>
void checkInput(int value) {
if (value < 0) {
throw std::invalid_argument("Negative value is not allowed.");
}
std::cout << "Valid input: " << value << std::endl;
}
int main() {
try {
int value;
std::cout << "Enter a number: ";
std::cin >> value;
checkInput(value);
} catch (const std::invalid_argument& e) {
std::cerr << "Invalid argument: " << e.what() << std::endl;
}
return 0;
}
演習問題3: ファイル読み込みと例外処理
ファイルを読み込み、空行が含まれていた場合に例外を投げるプログラムを作成してください。例外が発生した場合は、適切なメッセージを表示してください。
#include <iostream>
#include <fstream>
#include <stdexcept>
void readFile(const std::string& filename) {
std::ifstream file(filename);
if (!file) {
throw std::ios_base::failure("Failed to open file: " + filename);
}
std::string line;
while (std::getline(file, line)) {
if (line.empty()) {
throw std::runtime_error("Empty line detected.");
}
std::cout << line << std::endl;
}
file.close();
}
int main() {
try {
readFile("example.txt");
} catch (const std::runtime_error& e) {
std::cerr << "Runtime error: " << e.what() << std::endl;
} catch (const std::ios_base::failure& e) {
std::cerr << "File I/O error: " << e.what() << std::endl;
}
return 0;
}
演習問題4: カスタム例外クラスの作成
独自のカスタム例外クラスを作成し、特定のエラー条件に応じて例外を投げるプログラムを作成してください。
#include <iostream>
#include <exception>
class CustomException : public std::exception {
private:
std::string message;
public:
CustomException(const std::string& msg) : message(msg) {}
virtual const char* what() const noexcept override {
return message.c_str();
}
};
void processValue(int value) {
if (value < 0) {
throw CustomException("Negative value is not allowed.");
} else if (value == 0) {
throw CustomException("Zero is not a valid value.");
}
std::cout << "Processing value: " << value << std::endl;
}
int main() {
try {
processValue(0); // CustomException例外が投げられる
} catch (const CustomException& e) {
std::cerr << "Caught custom exception: " << e.what() << std::endl;
}
return 0;
}
これらの演習問題に取り組むことで、条件分岐と例外処理を組み合わせたエラーハンドリングの理解を深めることができます。次のセクションでは、この記事のまとめを行います。
まとめ
C++における条件分岐と例外処理を組み合わせたエラーハンドリングの方法について学びました。エラーハンドリングの基本概念から始まり、条件分岐、例外処理、カスタム例外クラスの作成、標準ライブラリの活用、そしてよくあるエラーパターンと対策、ファイル操作における具体例、演習問題までを網羅しました。
エラーハンドリングはプログラムの信頼性と安定性を向上させるために不可欠です。条件分岐と例外処理を適切に組み合わせることで、柔軟かつ強力なエラーハンドリングを実現できます。ぜひ今回学んだ知識を実際のプログラムに活用し、堅牢なコードを書いてください。
コメント