Qtの基礎 - 例外処理

提供:MochiuWiki : SUSE, EC, PCB
2024年9月10日 (火) 14:42時点におけるWiki (トーク | 投稿記録)による版 (→‎概要)
ナビゲーションに移動 検索に移動

概要

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

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;
 }



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

標準の設定では、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オプションが付加される。
この状態で、上記のサンプルコードを実行すると、例外をキャッチすることができる。