Qtの基礎 - 例外処理
概要
例外処理は、プログラムの実行中に発生する予期しないエラーや異常な状況を適切に管理するための重要な機能である。
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
オプションが付加される。
この状態で、上記のサンプルコードを実行すると、例外をキャッチすることができる。