「Qtの基礎 - 例外処理」の版間の差分

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動
137行目: 137行目:
   
   
  public:
  public:
     QDivideByZeroException(const QString &message = "Division by zero occurred") : m_message(message)
     QDivideByZeroException(const QString &message = "ゼロ除算が発生") : m_message(message)
     {}
     {}
   
   
165行目: 165行目:
  {
  {
     if (denominator == 0.0f) {
     if (denominator == 0.0f) {
         throw QDivideByZeroException("Attempted to divide by zero");
         throw QDivideByZeroException("ゼロで除算しようとしました");
     }
     }
   
   

2024年9月10日 (火) 14:52時点における版

概要

例外処理は、プログラムの実行中に発生する予期しないエラーや異常な状況を適切に管理するための重要な機能である。

Qtでは、C++の標準例外機構を基本として、Qt独自の拡張が加えられている。
try-catchブロックを使用して、潜在的に例外を投げる可能性のあるコードを囲み、発生した例外を捕捉する。

QExceptionクラスは、Qtフレームワーク内で使用される基本的な例外クラスである。
このクラスはstd::exceptionを継承しており、Qtに特化した例外を定義するための基盤となっている。

例外を投げる場合は、throwキーワードを使用する。

 if (<条件式>) {
    throw QException();
 }


これを捕捉するには、以下に示すように記述する。

 try {
    // 例外が発生する可能性のあるコード
 }
 catch (const QException &e) {
    // QExceptionの処理
 }
 catch (const std::exception &e) {
    // その他の標準C++例外の処理
 }
 catch (...) {
    // あらゆる例外の処理
 }


QExceptionクラスには、raiseメソッドがある。
これは、例外を再スローするのに便利であり、例外の連鎖を作成する場合に使用される。

また、Qtは、より具体的な例外クラスも提供している。
例えば、QNoMemory等があり、特定のエラー状況に対応している。

例外処理を適切に使用することにより、エラーの発生箇所と処理箇所を分離して、コードの可読性と保守性を向上させることができる。
ただし、過度な使用は避け、本当に必要な場合にのみ例外を使用することが推奨される。

また、Qtでは例外を完全に無効化することもできる。
これは、例外処理のオーバーヘッドを避ける場合や、例外をサポートしていないプラットフォームで開発する場合に有効である。


QExceptionクラス

QExceptionクラスとは

QExceptionクラスは、スレッド間で転送可能な例外の基底クラスを提供する。

QtConcurrentクラスは、QExceptionクラスを継承しており、2つのヘルパー関数を実装することにより、
スレッドの境界を越えて例外をスローおよびキャッチすることをサポートする。
QExceptionクラスを継承したサブクラスは、値によってスローされ、参照によってキャッチする必要がある。

 class SampleException : public QException
 {
 public:
    void raise() const override
    {
       throw *this;
    }
 
    SampleException *clone() const override
    {
       return new SampleException(*this);
    }
 };


 try
 {
    QtConcurrent::blockingMap(list, throwFunction); // throwFunctionはSampleExceptionをスローする関数とする
 }
 catch (SampleException &e)
 {
    // handle exception
    // ...略
 }


QExceptionクラスを継承したサブクラスではない例外を投げる場合、Qtの関数は受信側のスレッドでQUnhandledExceptionクラスを投げる。

QFutureクラスを使用する場合、以下に示す関数を呼び出すと、転送された例外がスローされる。

  • QFuture::waitForFinished()
  • QFuture::result()
  • QFuture::resultAt()
  • QFuture::results()


QExceptionクラスのメンバ関数

  • clone関数
 QException *QException::clone() const


QExceptionクラスを継承したサブクラスにおいて、clone関数を以下のようにオーバーライドする。

 SampleException *SampleException::clone() const
 {
    return new MyException(*this);
 }


  • raise関数
 void QException::raise() const


QExceptionクラスを継承したサブクラスにおいて、raise関数を以下のようにオーバーライドする。

 void MyException::raise() const
 {
    throw *this;
 }



QExceptionの使用例 : 0除算

以下の例では、QExceptionクラスを継承して、0除算で例外をスローするクラスを定義している。
また、safeDivide関数を定義して、0で除算した場合に、QDivideByZeroExceptionをスローする例を示している。

このクラスは、以下に示すような特徴を持つ。

  • コンストラクタでエラーメッセージを受け取る。 (デフォルトのメッセージもある)
  • raiseメソッドをオーバーライドして、例外を再スローできる。
  • cloneメソッドをオーバーライドして、例外オブジェクトの複製を可能にする。
  • whatメソッドをオーバーライドして、エラーメッセージを返す。


 // QDivideByZeroException.hファイル
 
 #include <QCoreApplication>
 #include <QException>
 
 class QDivideByZeroException : public QException
 {
 private:
    QString m_message;
 
 public:
    QDivideByZeroException(const QString &message = "ゼロ除算が発生") : m_message(message)
    {}
 
    void raise() const override
    {
       throw *this;
    }
 
    QDivideByZeroException *clone() const override
    {
       return new QDivideByZeroException(*this);
    }
 
    const char* what() const noexcept override
    {
       return m_message.toLocal8Bit().constData();
    }
 };


 // main.cppファイル
 
 #include "QDivideByZeroException.h"
 
 double safeDivide(double numerator, double denominator)
 {
    if (denominator == 0.0f) {
        throw QDivideByZeroException("ゼロで除算しようとしました");
    }
 
    return numerator / denominator;
 }
 
 int main()
 {
    try {
       double result = safeDivide(10.0, 0.0);
    }
    catch (const QDivideByZeroException &e) {
       qDebug() << "Caught exception:" << e.what();
    }
 
    return 0;
 }



全ての例外をキャッチする方法

標準の設定では、NULLポインタのアクセスや0除算等で発生する例外をキャッチすることができない。

 char *p = nullptr;
 try
 {
    qDebug() << "try";
    *p = 'A';
 }
 catch(...)
 {
    qDebug() << "catch";
 }


qmakeで生成されたMakefileを見ると、-EHscオプションが付加されている。
/EHsまたは/EHscを使用する場合、catch句では、非同期構造化例外がキャッチされない。

構造化例外とは、ハードウェア例外を含むシステム的な例外のことで、
Windowsでは、SEH(Structured Exception Handling : 構造化例外処理)という仕組みが提供されている。

全ての例外をキャッチするには、プロジェクトファイル(.pro)に、以下の設定を追記する。

win*
{
   QMAKE_CXXFLAGS_EXCEPTIONS_ON = /EHa
   QMAKE_CXXFLAGS_STL_ON = /EHa
}

linux*
{
   QMAKE_CXXFLAGS_EXCEPTIONS_ON = /EHa
   QMAKE_CXXFLAGS_STL_ON = /EHa
}


ビルドして生成されたMakefileには、上記で設定した-EHaオプションが付加される。
この状態で、上記のサンプルコードを実行すると、例外をキャッチすることができる。