web-dev-qa-db-ja.com

Qtのシグナルとスロットに関して、deleteとdeleteLaterはどのように機能しますか?

クラスQNetworkReplyのオブジェクトがあります。 finished()シグナルに接続されたスロット(他のオブジェクト)があります。信号は同期的です(デフォルト)。スレッドは1つだけです。

ある時点で、両方のオブジェクトを取り除きたいです。それらからの信号や何かはありません。消えてほしい。まあ、私は考えた、私は使用します

delete obj1; delete obj2;

しかし、私は本当にできますか? 〜QObjectの仕様は次のとおりです。

保留中のイベントが配信されるのを待っている間にQObjectを削除すると、クラッシュが発生する可能性があります。

「保留中のイベント」とは何ですか?つまり、deleteを呼び出している間に、配信される「保留中のイベント」がいくつかあり、それらがクラッシュを引き起こす可能性があり、実際に存在するかどうかを確認することはできませんか?

だから私が電話するとしましょう:

obj1->deleteLater(); obj2->deleteLater();

安全であるために。

しかし、私は本当に安全ですか? deleteLaterは、制御が到着したときにメインループで処理されるイベントを追加します。 obj1またはobj2の保留中のイベント(信号)が既に存在し、メインループで処理されるのを待っている可能性がありますbefore deleteLaterが処理されますか?それは非常に残念です。 「やや削除された」ステータスをチェックし、すべてのスロットの着信信号を無視するコードを記述したくありません。

72
stach

QObjectsを削除することは、2つの基本的なルールに従う場合、通常は安全です(つまり、通常の実践では、ATMに気付いていない病理学的ケースがあるかもしれません)。

  • 削除するオブジェクトからの(同期、接続タイプ「直接」)信号によって直接または間接的に呼び出されるスロットまたはメソッド内のオブジェクトを削除しないでください。例えば。シグナルOperation :: finished()とスロットManager :: operationFinished()を持つクラスOperationがある場合、そのスロットでシグナルを発行したオペレーションオブジェクトを削除したくないでしょう。 finished()シグナルを発行するメソッドは、発行後も「this」へのアクセスを続け(メンバーへのアクセスなど)、無効な「this」ポインターを操作します。

  • 同様に、オブジェクトのイベントハンドラーから同期的に呼び出されるコード内のオブジェクトを削除しないでください。例えば。 SomeWidget :: fooEvent()またはそこから呼び出すメソッド/スロットでSomeWidgetを削除しないでください。イベントシステムは、既に削除されたオブジェクトで動作し続けます->クラッシュ。

バックトレースは通常奇妙に見えるので、両方とも追跡するのが難しい場合があります(PODメンバー変数へのアクセス中のクラッシュのように)。削除されるオブジェクト。

このような場合は、deleteLater()の最も一般的な使用例です。コントロールがイベントループに戻ってからオブジェクトを削除する前に、現在のイベントを完了できるようにします。別の方法として、キュー接続/ QMetaObject :: invokeMethod(...、Qt :: QueuedConnection)を使用してアクション全体を延期する方がよい場合がよくあります。

66
Frank Osterfeld

参照ドキュメントの次の2行に答えが記載されています。

〜QObject から、

保留中のイベントが配信されるのを待っている間にQObjectを削除すると、クラッシュする可能性があります。 QObjectが現在実行中のスレッドとは異なるスレッドに存在する場合、直接削除しないでください。代わりにdeleteLater()を使用すると、イベントループが発生します。保留中のイベントがすべて配信された後にオブジェクトを削除します。

特に、他のスレッドから削除しないように指示しています。シングルスレッドアプリケーションがあるため、QObjectを削除しても安全です。

また、マルチスレッド環境で削除する必要がある場合は、deleteLater()を使用します。これにより、すべてのイベントの処理が完了したら、QObjectが削除されます。

20
liaK

Delta Object Rules のいずれかについて読んでいる質問への答えを見つけることができます:

シグナルセーフ(SS)。
信号の1つによって呼び出されるスロット内から、デストラクタを含むオブジェクトのメソッドを安全に呼び出す必要があります。

断片:

コアでは、QObjectはシグナリング中の削除をサポートしています。これを利用するには、オブジェクトが削除された後に自身のメンバーにアクセスしようとしていないことを確認する必要があります。ただし、ほとんどのQtオブジェクトはこの方法で記述されていないため、それらを記述する必要はありません。このため、シグナルの1つでオブジェクトを削除する必要がある場合は、deleteLater()を常に呼び出すことをお勧めします。「delete」はアプリケーションをクラッシュさせる可能性があるためです。

残念ながら、いつ「delete」とdeleteLater()を使用すべきかは必ずしも明確ではありません。つまり、コードパスに信号ソースがあることは必ずしも明らかではありません。多くの場合、現在安全な一部のオブジェクトで「削除」を使用するコードブロックがある場合がありますが、将来のある時点で、この同じコードブロックが信号ソースから呼び出され、アプリケーションが突然クラッシュします。この問題の唯一の一般的な解決策は、一見不要であるように思われる場合でも、deleteLater()を常に使用することです。

一般に、Delta Object Rulesは、すべてのQt開発者にとって必須の読み取りであると考えています。優れた読み物です。

14
Piotr Dobrogost

私の知る限り、これは主にオブジェクトが異なるスレッドに存在する場合の問題です。または、実際に信号を処理している間に。

そうでない場合、QObjectを削除すると、最初にすべてのシグナルとスロットが切断され、保留中のイベントがすべて削除されます。 disconnect()の呼び出しが行うように。

2