この問題は長い間解決されており、Qt には慣用的な解決策があります。
すべてのスロット呼び出しは、最終的に次のいずれかから発生します。
イベントハンドラ、例えば:
あなたが完全に制御できるコード、例えば:
main
、または from QThread::run
、またはfromの実装でシグナルを発信する場合QRunnable::run
。
オブジェクト内のイベント ハンドラは、常に を通じて到達しQCoreApplication::notify
ます。したがって、アプリケーション クラスをサブクラス化し、notify メソッドを再実装するだけです。
これは、イベント ハンドラーから発生するすべてのシグナル スロット呼び出しに影響します。具体的には:
イベントハンドラから発生したすべてのシグナルとそれらに直接接続されたスロット
これにより、信号ごとのコストではなく、スロットごとのコストではなく、イベントごとのコストが追加されます。なぜ違いが重要なのですか?多くのコントロールは、1 つのイベントごとに複数のシグナルを発行します。はQPushButton
、 に反応して、 、または、およびをすべて同じイベントからQMouseEvent
放出できます。複数のシグナルが発行されているにもかかわらず、が呼び出されたのは 1 回だけです。clicked(bool)
pressed()
released()
toggled(bool)
notify
キューに入れられたすべてのスロット呼び出しとメソッド呼び出し
これらはQMetaCallEvent
、レシーバー オブジェクトに a をディスパッチすることによって実装されます。呼び出しは によって実行されQObject::event
ます。イベント配信が絡むのでnotify
使用します。コストはコール呼び出しごとです (したがって、スロットごとです)。このコストは、必要に応じて簡単に軽減できます (実装を参照)。
イベントハンドラーからではなく、たとえばmain
関数の内部からシグナルを発信し、スロットが直接接続されている場合、この処理方法は明らかに機能しません。シグナルの発信を試してラップする必要があります。 /キャッチブロック。
QCoreApplication::notify
は配信されたすべてのイベントに対して呼び出されるため、このメソッドの唯一のオーバーヘッドは、try/catch ブロックと基本実装のメソッド呼び出しのコストです。後者は小さい。
前者は、マークされたオブジェクトに通知をラップするだけで軽減できます。これは、オブジェクトのサイズを犠牲にすることなく、補助データ構造のルックアップを伴わずに行う必要があります。これらの余分なコストは、例外がスローされない try/catch ブロックのコストを超えます。
「マーク」はオブジェクト自体から取得する必要があります。そこに可能性があります: QObject::d_ptr->unused
. 残念ながら、これはそうではありません。なぜなら、そのメンバーはオブジェクトのコンストラクターで初期化されていないため、ゼロに設定されていることに依存することはできません。このようなマークを使用するソリューションでは、Qt 自体を少し変更する必要があります (unused = 0;
行を に追加QObjectPrivate::QObjectPrivate
)。
コード:
template <typename BaseApp> class SafeNotifyApp : public BaseApp {
bool m_wrapMetaCalls;
public:
SafeNotifyApp(int & argc, char ** argv) :
BaseApp(argc, argv), m_wrapMetaCalls(false) {}
void setWrapMetaCalls(bool w) { m_wrapMetaCalls = w; }
bool doesWrapMetaCalls() const { return m_wrapMetaCalls; }
bool notify(QObject * receiver, QEvent * e) Q_DECL_OVERRIDE {
if (! m_wrapMetaCalls && e->type() == QEvent::MetaCall) {
// This test is presumed to have a lower cost than the try-catch
return BaseApp::notify(receiver, e);
}
try {
return BaseApp::notify(receiver, e);
}
catch (const std::bad_alloc&) {
// do something clever
}
}
};
int main(int argc, char ** argv) {
SafeNotifyApp<QApplication> a(argc, argv);
...
}
特定の状況で を処理することが意味があるかどうかを完全に無視していることに注意してくださいstd::bad_alloc
。単にそれを処理するだけでは、例外の安全性にはなりません。