C++の関数tryブロックの使い方を徹底解説

C++の例外処理機能である関数tryブロックは、エラーハンドリングを効果的に行うための強力なツールです。本記事では、関数tryブロックの基本的な使い方から応用までを詳細に解説します。初めて関数tryブロックに触れる方でも理解しやすいように、具体的なコード例を交えながら説明します。この記事を通じて、C++の例外処理をマスターし、より堅牢なプログラムを作成する手助けをします。

目次

関数tryブロックとは

関数tryブロックは、C++の例外処理機能の一部であり、関数全体をtryブロックとして扱うことができる構文です。通常のtry-catchブロックは、コード内の特定のセクションに対して例外処理を行いますが、関数tryブロックは関数の宣言部分で例外処理を行うため、関数の初期化リストやコンストラクター、デストラクターで発生する例外もキャッチできます。

関数tryブロックの構文は次のようになります:

class MyClass {
public:
    MyClass() try : member1(), member2() {
        // コンストラクタの本体
    } catch (const std::exception& e) {
        // 例外処理
        std::cerr << "Constructor exception: " << e.what() << std::endl;
    }

private:
    MemberClass1 member1;
    MemberClass2 member2;
};

この構文により、関数内で発生するすべての例外を一括してキャッチし、適切に処理することが可能になります。関数tryブロックを利用することで、より安全で管理しやすいコードを書くことができます。

基本的な使い方

関数tryブロックの基本的な使い方を理解するために、まずは基本的な構文と簡単なコード例を紹介します。関数tryブロックは、関数の宣言部分で例外処理を定義することができ、特にコンストラクタでの使用が一般的です。

基本的な構文は以下の通りです:

class Example {
public:
    Example() try : member1(), member2() {
        // コンストラクタの本体
        std::cout << "Constructor body\n";
    } catch (const std::exception& e) {
        // 例外処理
        std::cerr << "Constructor exception: " << e.what() << std::endl;
    }

private:
    MemberClass1 member1;
    MemberClass2 member2;
};

このコード例では、Exampleクラスのコンストラクタ内で、member1member2の初期化中に発生する例外をキャッチするように構成されています。

基本的な使い方のポイント

  1. 関数宣言でのtryブロック
    関数の宣言部分でtryキーワードを使用し、例外処理を行いたいコードを含むブロックを指定します。
  2. catchブロック
    catchブロックで例外をキャッチし、適切な処理を行います。通常のtry-catchブロックと同様に、特定の例外型をキャッチすることができます。
  3. 初期化リスト
    関数tryブロックは、初期化リストでの例外もキャッチするため、メンバーの初期化中に発生する例外を安全に処理できます。

簡単なコード例

以下は、関数tryブロックを使用した簡単なコード例です:

#include <iostream>
#include <stdexcept>

class MemberClass {
public:
    MemberClass() {
        // 初期化中に例外をスロー
        throw std::runtime_error("Initialization failed");
    }
};

class Example {
public:
    Example() try : member() {
        std::cout << "Constructor body\n";
    } catch (const std::exception& e) {
        std::cerr << "Constructor exception: " << e.what() << std::endl;
    }

private:
    MemberClass member;
};

int main() {
    try {
        Example example;
    } catch (const std::exception& e) {
        std::cerr << "Main exception: " << e.what() << std::endl;
    }
    return 0;
}

この例では、MemberClassの初期化中に例外が発生し、Exampleクラスのコンストラクタ内でその例外がキャッチされます。これにより、例外が発生してもプログラムが安全に動作を続けることができます。

関数tryブロックの利点

関数tryブロックを使用することで、C++プログラムの例外処理がより効果的かつ安全になります。以下では、関数tryブロックの主な利点について説明します。

1. 初期化リストの例外処理

関数tryブロックを使用することで、クラスのメンバー初期化リストで発生する例外をキャッチできます。通常のtry-catchブロックでは、初期化リスト内の例外をキャッチすることはできませんが、関数tryブロックを使えば可能です。

class MyClass {
public:
    MyClass() try : member1(), member2() {
        // コンストラクタの本体
    } catch (const std::exception& e) {
        // 初期化リストで発生した例外をキャッチ
        std::cerr << "Initialization exception: " << e.what() << std::endl;
    }

private:
    MemberClass1 member1;
    MemberClass2 member2;
};

2. コンストラクタ全体の例外処理

関数tryブロックを使用すると、コンストラクタ全体で発生する例外を一箇所で処理することができます。これにより、コードの見通しが良くなり、例外処理が一元化されます。

3. デストラクタでの例外処理

関数tryブロックはデストラクタにも適用できます。デストラクタ内で発生する例外を安全にキャッチし、リソースの解放や後処理を適切に行うことが可能です。

class MyClass {
public:
    ~MyClass() try {
        // デストラクタの本体
    } catch (const std::exception& e) {
        // デストラクタ内で発生した例外をキャッチ
        std::cerr << "Destructor exception: " << e.what() << std::endl;
    }
};

4. より堅牢なコード

関数tryブロックを使用することで、プログラムが予期せぬ例外によってクラッシュするのを防ぎ、堅牢性が向上します。特に、クラスのメンバーの初期化や解放時に発生する可能性のある例外を確実に処理できるため、信頼性の高いコードを書くことができます。

5. コードの可読性向上

関数tryブロックを用いると、例外処理コードが明確に分離されるため、コードの可読性が向上します。これにより、メンテナンスが容易になり、バグの発見と修正が迅速に行えるようになります。

関数tryブロックを適切に活用することで、C++プログラムの例外処理が効率的になり、コードの安全性と可読性が向上します。次に、具体的なコード例を通じてさらに理解を深めていきましょう。

具体的なコード例

ここでは、関数tryブロックを使用した具体的なコード例を示し、その動作を詳細に解説します。これにより、関数tryブロックの実践的な使い方がより明確になるでしょう。

例1: コンストラクタでの例外処理

以下のコードは、コンストラクタ内で発生する例外を関数tryブロックを用いてキャッチし、適切に処理する例です。

#include <iostream>
#include <stdexcept>

class MemberClass {
public:
    MemberClass() {
        // 初期化中に例外をスロー
        throw std::runtime_error("MemberClass initialization failed");
    }
};

class Example {
public:
    Example() try : member() {
        std::cout << "Constructor body\n";
    } catch (const std::exception& e) {
        std::cerr << "Constructor exception: " << e.what() << std::endl;
    }

private:
    MemberClass member;
};

int main() {
    try {
        Example example;
    } catch (const std::exception& e) {
        std::cerr << "Main exception: " << e.what() << std::endl;
    }
    return 0;
}

解説:

  • MemberClassのコンストラクタ内で例外がスローされます。
  • Exampleクラスのコンストラクタは関数tryブロックを使用しており、初期化リストのmemberの初期化中に発生する例外をキャッチします。
  • コンストラクタのcatchブロックで例外を処理し、エラーメッセージを出力します。

例2: デストラクタでの例外処理

次のコードは、デストラクタ内で発生する例外を関数tryブロックでキャッチする例です。

#include <iostream>
#include <stdexcept>

class Example {
public:
    ~Example() try {
        // デストラクタの本体
        throw std::runtime_error("Destructor failure");
    } catch (const std::exception& e) {
        std::cerr << "Destructor exception: " << e.what() << std::endl;
    }
};

int main() {
    try {
        Example example;
    } catch (const std::exception& e) {
        std::cerr << "Main exception: " << e.what() << std::endl;
    }
    return 0;
}

解説:

  • Exampleクラスのデストラクタ内で例外がスローされます。
  • デストラクタも関数tryブロックを使用しており、デストラクタのcatchブロックで例外をキャッチし、エラーメッセージを出力します。

例3: 複数の例外をキャッチする

以下のコードは、関数tryブロック内で複数の例外型をキャッチする例です。

#include <iostream>
#include <stdexcept>

class Example {
public:
    Example() try : member1(), member2() {
        std::cout << "Constructor body\n";
    } catch (const std::runtime_error& e) {
        std::cerr << "Runtime error: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }

private:
    MemberClass1 member1;
    MemberClass2 member2;
};

class MemberClass1 {
public:
    MemberClass1() {
        // 初期化中に例外をスロー
        throw std::runtime_error("MemberClass1 initialization failed");
    }
};

class MemberClass2 {
public:
    MemberClass2() {
        // 初期化中に例外をスロー
        throw std::logic_error("MemberClass2 initialization failed");
    }
};

int main() {
    try {
        Example example;
    } catch (const std::exception& e) {
        std::cerr << "Main exception: " << e.what() << std::endl;
    }
    return 0;
}

解説:

  • MemberClass1およびMemberClass2の初期化中に異なる例外がスローされます。
  • Exampleクラスのコンストラクタは関数tryブロックを使用しており、catchブロックでそれぞれ異なる例外をキャッチします。
  • catchブロックで例外を個別に処理し、適切なエラーメッセージを出力します。

これらの例を通じて、関数tryブロックの基本的な使い方とその利点を理解することができます。次に、関数tryブロックを使用したより複雑なシナリオについて説明します。

例外のスローとキャッチ

関数tryブロック内での例外のスローとキャッチの方法について説明します。C++では、例外をスロー(throw)することで、エラー状態を通知し、catchブロックでこれをキャッチして適切に処理することができます。

例外のスロー

例外をスローするには、throwキーワードを使用します。以下は、関数内で例外をスローする簡単な例です:

void riskyFunction() {
    throw std::runtime_error("An error occurred");
}

この関数が呼び出されると、std::runtime_error型の例外がスローされます。

例外のキャッチ

スローされた例外は、catchブロックでキャッチして処理することができます。関数tryブロックを使用して、関数全体の例外をキャッチする例を見てみましょう:

#include <iostream>
#include <stdexcept>

class Example {
public:
    Example() try {
        riskyFunction();
    } catch (const std::runtime_error& e) {
        std::cerr << "Runtime error: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }

private:
    void riskyFunction() {
        throw std::runtime_error("An error occurred in riskyFunction");
    }
};

int main() {
    try {
        Example example;
    } catch (const std::exception& e) {
        std::cerr << "Main exception: " << e.what() << std::endl;
    }
    return 0;
}

解説:

  • Exampleクラスのコンストラクタ内でriskyFunctionが呼び出され、例外がスローされます。
  • コンストラクタの関数tryブロックで例外がキャッチされ、適切なエラーメッセージが出力されます。
  • catchブロックは複数の例外型をキャッチするため、特定の例外型に対して適切な処理を行うことができます。

複数の例外をキャッチする

関数tryブロック内で複数の例外をキャッチする場合、各例外型に対して別々のcatchブロックを用意します。以下の例では、std::runtime_errorstd::logic_errorの両方をキャッチしています:

#include <iostream>
#include <stdexcept>

class Example {
public:
    Example() try {
        riskyFunction();
    } catch (const std::runtime_error& e) {
        std::cerr << "Runtime error: " << e.what() << std::endl;
    } catch (const std::logic_error& e) {
        std::cerr << "Logic error: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }

private:
    void riskyFunction() {
        throw std::runtime_error("An error occurred in riskyFunction");
    }
};

int main() {
    try {
        Example example;
    } catch (const std::exception& e) {
        std::cerr << "Main exception: " << e.what() << std::endl;
    }
    return 0;
}

解説:

  • Exampleクラスのコンストラクタ内でスローされる可能性のある異なる例外型を、対応するcatchブロックでキャッチしています。
  • 各catchブロックで異なる処理を行うことで、エラーの種類に応じた適切な対処が可能です。

まとめ

関数tryブロックを使用することで、関数全体の例外処理が一元化され、初期化リストやコンストラクタ、デストラクタ内で発生する例外も安全に処理することができます。例外のスローとキャッチの方法を理解し、適切に使用することで、C++プログラムの堅牢性と信頼性が大幅に向上します。次に、関数tryブロックを使った複数の例外ハンドリングについてさらに詳しく見ていきましょう。

複数の例外ハンドリング

関数tryブロックを使用することで、複数の種類の例外を効果的にハンドリングすることができます。これは特に複雑なシステムやライブラリを扱う場合に有用です。以下では、複数の例外をどのように処理するかについて具体的な例を示しながら説明します。

例1: 異なる例外型のハンドリング

異なる例外型をキャッチするために、複数のcatchブロックを使用します。以下の例では、std::runtime_errorstd::logic_errorの両方をキャッチしています:

#include <iostream>
#include <stdexcept>

class Example {
public:
    Example() try {
        functionThatThrows();
    } catch (const std::runtime_error& e) {
        std::cerr << "Runtime error: " << e.what() << std::endl;
    } catch (const std::logic_error& e) {
        std::cerr << "Logic error: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }

private:
    void functionThatThrows() {
        throw std::runtime_error("A runtime error occurred");
    }
};

int main() {
    try {
        Example example;
    } catch (const std::exception& e) {
        std::cerr << "Main exception: " << e.what() << std::endl;
    }
    return 0;
}

解説:

  • functionThatThrowsメソッドでstd::runtime_errorがスローされます。
  • コンストラクタ内の関数tryブロックで、異なるcatchブロックを使って複数の例外型をキャッチしています。
  • 各catchブロックで例外の種類に応じた適切なエラーメッセージが出力されます。

例2: 基底クラスの例外ハンドリング

複数の例外型が同じ基底クラス(例:std::exception)を持つ場合、基底クラスのcatchブロックを使って一括してキャッチすることもできます:

#include <iostream>
#include <stdexcept>

class Example {
public:
    Example() try {
        functionThatThrows();
    } catch (const std::exception& e) {
        std::cerr << "Exception caught: " << e.what() << std::endl;
    }

private:
    void functionThatThrows() {
        throw std::logic_error("A logic error occurred");
    }
};

int main() {
    try {
        Example example;
    } catch (const std::exception& e) {
        std::cerr << "Main exception: " << e.what() << std::endl;
    }
    return 0;
}

解説:

  • functionThatThrowsメソッドでstd::logic_errorがスローされます。
  • 基底クラスであるstd::exceptionのcatchブロックで例外をキャッチし、エラーメッセージを出力します。

例3: ユーザー定義例外のハンドリング

ユーザー定義の例外クラスを使用することで、より具体的なエラーハンドリングを実現できます:

#include <iostream>
#include <stdexcept>

class CustomException : public std::exception {
public:
    const char* what() const noexcept override {
        return "Custom exception occurred";
    }
};

class Example {
public:
    Example() try {
        functionThatThrows();
    } catch (const CustomException& e) {
        std::cerr << "CustomException caught: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception caught: " << e.what() << std::endl;
    }

private:
    void functionThatThrows() {
        throw CustomException();
    }
};

int main() {
    try {
        Example example;
    } catch (const std::exception& e) {
        std::cerr << "Main exception: " << e.what() << std::endl;
    }
    return 0;
}

解説:

  • CustomExceptionクラスを定義し、std::exceptionを継承しています。
  • functionThatThrowsメソッドでCustomExceptionがスローされます。
  • Exampleクラスのコンストラクタ内で、CustomExceptionとその他のstd::exception型の例外をキャッチしています。

まとめ

関数tryブロックを使用することで、複数の例外型を効果的にハンドリングし、プログラムの堅牢性を高めることができます。複数のcatchブロックを使用することで、特定の例外型に対して適切な処理を行い、基底クラスのcatchブロックを使用することで、共通の例外処理を一括して行うことが可能です。次に、実践的な応用例を通じて、関数tryブロックのさらなる活用方法を学びましょう。

実践的な応用例

関数tryブロックの概念を理解したところで、実際のプログラムでどのように活用するかを具体的なシナリオを通じて見ていきましょう。ここでは、データベース接続やファイル操作といった実践的な例を取り上げます。

例1: データベース接続の管理

データベース接続を管理するクラスを例に、関数tryブロックを使用して初期化時の例外をキャッチし、適切に処理します。

#include <iostream>
#include <stdexcept>

class DatabaseConnection {
public:
    DatabaseConnection() try {
        // データベースへの接続を確立
        connect();
    } catch (const std::runtime_error& e) {
        std::cerr << "Database connection error: " << e.what() << std::endl;
        // 必要に応じてリソースを解放
        cleanup();
        throw; // 例外を再スロー
    }

    ~DatabaseConnection() {
        // デストラクタで接続を閉じる
        disconnect();
    }

private:
    void connect() {
        // 接続確立処理
        throw std::runtime_error("Failed to connect to database");
    }

    void disconnect() {
        // 接続解放処理
        std::cout << "Disconnected from database\n";
    }

    void cleanup() {
        // クリーンアップ処理
        std::cout << "Cleanup after failed connection\n";
    }
};

int main() {
    try {
        DatabaseConnection db;
    } catch (const std::exception& e) {
        std::cerr << "Main exception: " << e.what() << std::endl;
    }
    return 0;
}

解説:

  • DatabaseConnectionクラスのコンストラクタでデータベース接続を確立しようとしますが、接続失敗時に例外をスローします。
  • 関数tryブロックで例外をキャッチし、エラーメッセージを表示しつつ、必要なクリーンアップ処理を実行します。
  • デストラクタで接続を解放します。

例2: ファイル操作

ファイル操作を行うクラスを例に、関数tryブロックを使用してファイルオープン時の例外をキャッチします。

#include <iostream>
#include <fstream>
#include <stdexcept>

class FileHandler {
public:
    FileHandler(const std::string& filename) try : file(filename) {
        if (!file.is_open()) {
            throw std::runtime_error("Failed to open file: " + filename);
        }
    } catch (const std::exception& e) {
        std::cerr << "FileHandler exception: " << e.what() << std::endl;
        throw; // 例外を再スロー
    }

    ~FileHandler() {
        if (file.is_open()) {
            file.close();
            std::cout << "File closed\n";
        }
    }

private:
    std::ifstream file;
};

int main() {
    try {
        FileHandler fh("nonexistent_file.txt");
    } catch (const std::exception& e) {
        std::cerr << "Main exception: " << e.what() << std::endl;
    }
    return 0;
}

解説:

  • FileHandlerクラスのコンストラクタでファイルを開こうとしますが、ファイルが存在しない場合に例外をスローします。
  • 関数tryブロックで例外をキャッチし、エラーメッセージを表示します。
  • デストラクタでファイルが開いている場合に閉じます。

例3: ネットワーク接続

ネットワーク接続を管理するクラスを例に、関数tryブロックを使用して接続確立時の例外をキャッチします。

#include <iostream>
#include <stdexcept>

class NetworkConnection {
public:
    NetworkConnection() try {
        // ネットワーク接続を確立
        connect();
    } catch (const std::runtime_error& e) {
        std::cerr << "Network connection error: " << e.what() << std::endl;
        cleanup();
        throw; // 例外を再スロー
    }

    ~NetworkConnection() {
        // デストラクタで接続を閉じる
        disconnect();
    }

private:
    void connect() {
        // 接続確立処理
        throw std::runtime_error("Failed to connect to network");
    }

    void disconnect() {
        // 接続解放処理
        std::cout << "Disconnected from network\n";
    }

    void cleanup() {
        // クリーンアップ処理
        std::cout << "Cleanup after failed connection\n";
    }
};

int main() {
    try {
        NetworkConnection nc;
    } catch (const std::exception& e) {
        std::cerr << "Main exception: " << e.what() << std::endl;
    }
    return 0;
}

解説:

  • NetworkConnectionクラスのコンストラクタでネットワーク接続を確立しようとしますが、接続失敗時に例外をスローします。
  • 関数tryブロックで例外をキャッチし、エラーメッセージを表示しつつ、必要なクリーンアップ処理を実行します。
  • デストラクタで接続を解放します。

まとめ

これらの実践的な応用例を通じて、関数tryブロックがどのように使用されるかを具体的に理解できたと思います。関数tryブロックを適切に活用することで、初期化リストやコンストラクタ、デストラクタ内で発生する例外を安全に処理し、プログラムの堅牢性と可読性を向上させることができます。次に、例外の再スローについてさらに詳しく説明します。

例外の再スロー

関数tryブロックでキャッチした例外を再スローする方法とその使いどころについて説明します。例外の再スローは、例外を一度キャッチしてから再度スローすることで、呼び出し元に例外を伝える場合に使用します。これは、例外のログを取る、リソースを解放するなど、追加の処理を行いたい場合に有効です。

例外の再スローの基本

例外を再スローするには、throwキーワードを使います。catchブロック内で例外を再スローすることで、例外を上位の呼び出し元に伝えることができます。

#include <iostream>
#include <stdexcept>

class Example {
public:
    Example() try {
        riskyFunction();
    } catch (const std::exception& e) {
        std::cerr << "Caught in constructor: " << e.what() << std::endl;
        throw; // 例外を再スロー
    }

private:
    void riskyFunction() {
        throw std::runtime_error("An error occurred in riskyFunction");
    }
};

int main() {
    try {
        Example example;
    } catch (const std::exception& e) {
        std::cerr << "Caught in main: " << e.what() << std::endl;
    }
    return 0;
}

解説:

  • riskyFunctionで例外がスローされます。
  • Exampleクラスのコンストラクタ内でその例外をキャッチし、エラーメッセージを出力します。
  • throwキーワードで例外を再スローし、main関数で再びキャッチします。

例1: リソースの解放と再スロー

リソースの解放やログの記録などの追加処理を行った後、例外を再スローする例です。

#include <iostream>
#include <stdexcept>

class ResourceHandler {
public:
    ResourceHandler() try {
        acquireResource();
    } catch (const std::exception& e) {
        std::cerr << "Error acquiring resource: " << e.what() << std::endl;
        releaseResource();
        throw; // 例外を再スロー
    }

    ~ResourceHandler() {
        releaseResource();
    }

private:
    void acquireResource() {
        // リソース取得処理
        throw std::runtime_error("Failed to acquire resource");
    }

    void releaseResource() {
        // リソース解放処理
        std::cout << "Resource released\n";
    }
};

int main() {
    try {
        ResourceHandler handler;
    } catch (const std::exception& e) {
        std::cerr << "Caught in main: " << e.what() << std::endl;
    }
    return 0;
}

解説:

  • ResourceHandlerクラスのコンストラクタでリソース取得を試みますが、例外が発生します。
  • 例外をキャッチし、エラーメッセージを表示した後、リソースを解放し、例外を再スローします。
  • デストラクタでリソースを解放するため、安全なリソース管理が実現されます。

例2: コンストラクタチェーンでの例外再スロー

複数のコンストラクタがチェーン状に呼び出される場合、各コンストラクタで例外を再スローし、最終的に呼び出し元でキャッチする例です。

#include <iostream>
#include <stdexcept>

class Base {
public:
    Base() try {
        initializeBase();
    } catch (const std::exception& e) {
        std::cerr << "Base constructor caught: " << e.what() << std::endl;
        throw;
    }

private:
    void initializeBase() {
        throw std::runtime_error("Base initialization failed");
    }
};

class Derived : public Base {
public:
    Derived() try {
        initializeDerived();
    } catch (const std::exception& e) {
        std::cerr << "Derived constructor caught: " << e.what() << std::endl;
        throw;
    }

private:
    void initializeDerived() {
        // Derivedクラスの初期化処理
    }
};

int main() {
    try {
        Derived derived;
    } catch (const std::exception& e) {
        std::cerr << "Caught in main: " << e.what() << std::endl;
    }
    return 0;
}

解説:

  • Baseクラスのコンストラクタで例外が発生します。
  • 例外をキャッチし、メッセージを表示してから再スローします。
  • Derivedクラスのコンストラクタでも例外をキャッチし、再スローします。
  • 最終的に、main関数で例外をキャッチして処理します。

まとめ

例外の再スローは、例外処理の際に追加の処理(ログ記録、リソース解放など)を行いたい場合に非常に有用です。再スローすることで、例外の伝播を継続し、上位の呼び出し元に例外を通知することができます。これにより、堅牢で管理しやすいコードを書くことが可能になります。次に、関数tryブロックの理解を深めるための演習問題を提供します。

演習問題

関数tryブロックの理解を深めるために、いくつかの演習問題を提供します。これらの問題を通じて、関数tryブロックの基本的な使い方や応用方法を実際に体験してください。

演習問題1: コンストラクタでの例外処理

問題:

次のコードにおいて、コンストラクタ内で例外が発生した場合に、その例外をキャッチしてエラーメッセージを表示し、さらに例外を再スローするように修正してください。

#include <iostream>
#include <stdexcept>

class MyClass {
public:
    MyClass() {
        // コンストラクタの本体で例外をスロー
        throw std::runtime_error("Initialization error in MyClass");
    }
};

int main() {
    try {
        MyClass obj;
    } catch (const std::exception& e) {
        std::cerr << "Caught in main: " << e.what() << std::endl;
    }
    return 0;
}

解答例:

#include <iostream>
#include <stdexcept>

class MyClass {
public:
    MyClass() try {
        // コンストラクタの本体で例外をスロー
        throw std::runtime_error("Initialization error in MyClass");
    } catch (const std::exception& e) {
        std::cerr << "Constructor caught: " << e.what() << std::endl;
        throw; // 例外を再スロー
    }
};

int main() {
    try {
        MyClass obj;
    } catch (const std::exception& e) {
        std::cerr << "Caught in main: " << e.what() << std::endl;
    }
    return 0;
}

演習問題2: デストラクタでの例外処理

問題:

次のコードにおいて、デストラクタ内で例外が発生した場合に、その例外をキャッチしてエラーメッセージを表示するように修正してください。

#include <iostream>
#include <stdexcept>

class MyClass {
public:
    ~MyClass() {
        // デストラクタの本体で例外をスロー
        throw std::runtime_error("Error in destructor of MyClass");
    }
};

int main() {
    try {
        MyClass obj;
    } catch (const std::exception& e) {
        std::cerr << "Caught in main: " << e.what() << std::endl;
    }
    return 0;
}

解答例:

#include <iostream>
#include <stdexcept>

class MyClass {
public:
    ~MyClass() try {
        // デストラクタの本体で例外をスロー
        throw std::runtime_error("Error in destructor of MyClass");
    } catch (const std::exception& e) {
        std::cerr << "Destructor caught: " << e.what() << std::endl;
    }
};

int main() {
    try {
        MyClass obj;
    } catch (const std::exception& e) {
        std::cerr << "Caught in main: " << e.what() << std::endl;
    }
    return 0;
}

演習問題3: 複数の例外ハンドリング

問題:

次のコードにおいて、std::runtime_errorstd::logic_errorの両方をキャッチし、それぞれ異なるエラーメッセージを表示するように修正してください。

#include <iostream>
#include <stdexcept>

class MyClass {
public:
    MyClass() {
        // コンストラクタの本体で例外をスロー
        throw std::runtime_error("Initialization error in MyClass");
    }
};

int main() {
    try {
        MyClass obj;
    } catch (const std::exception& e) {
        std::cerr << "Caught in main: " << e.what() << std::endl;
    }
    return 0;
}

解答例:

#include <iostream>
#include <stdexcept>

class MyClass {
public:
    MyClass() try {
        // コンストラクタの本体で例外をスロー
        throw std::runtime_error("Initialization error in MyClass");
    } catch (const std::runtime_error& e) {
        std::cerr << "Runtime error caught: " << e.what() << std::endl;
        throw;
    } catch (const std::logic_error& e) {
        std::cerr << "Logic error caught: " << e.what() << std::endl;
        throw;
    } catch (const std::exception& e) {
        std::cerr << "General exception caught: " << e.what() << std::endl;
        throw;
    }
};

int main() {
    try {
        MyClass obj;
    } catch (const std::exception& e) {
        std::cerr << "Caught in main: " << e.what() << std::endl;
    }
    return 0;
}

まとめ

これらの演習問題を解くことで、関数tryブロックの使い方に慣れ、例外処理のスキルを向上させることができます。関数tryブロックを適切に使用することで、より堅牢で管理しやすいコードを書くことができるようになります。次に、この記事の内容をまとめます。

まとめ

本記事では、C++の関数tryブロックについて、その基本的な使い方から応用まで詳しく解説しました。関数tryブロックを使用することで、クラスのコンストラクタやデストラクタにおいて例外を安全に処理し、プログラムの堅牢性と可読性を向上させることができます。具体的なコード例を通じて、実践的なシナリオでの関数tryブロックの活用方法を学びました。また、演習問題を解くことで、関数tryブロックの使用に慣れ、実際のプログラムに適用する自信を深めることができたと思います。これらの知識を活用し、より安全で効率的なC++プログラムを作成してください。

コメント

コメントする

目次