81

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

ある時点で、両方のオブジェクトを削除したいと思います。これ以上の信号やそれらからの何かはありません。私は彼らがいなくなって欲しい。まあ、私は思った、私は使用します

delete obj1; delete obj2;

しかし、私は本当にできますか?〜QObjectの仕様は次のように述べています。

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

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

だから私が呼ぶとしましょう:

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

安全であるために。

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

4

4 に答える 4

74

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

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

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

バックトレースは通常奇妙に見えるため(PODメンバー変数へのアクセス中のクラッシュのように)、特に、元々からのシグナルまたはイベントによって開始された数ステップ下で削除が発生する可能性がある複雑なシグナル/スロットチェーンがある場合は、両方を追跡するのが難しい場合があります削除されるオブジェクト。

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

于 2011-02-03T17:22:29.927 に答える
23

参照されたドキュメントの次の2行は、答えを示しています。

〜QObjectから、

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

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

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

于 2011-02-04T05:17:53.713 に答える
14

あなたはこれを述べているデルタオブジェクトルールの1つについて読んであなたの質問への答えを見つけることができます:

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

断片:

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

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

一般的に、私はDeltaObjectRulesをすべてのQt開発者にとって必須の読み物と見なしています。優れた読み物です。

于 2011-02-03T20:55:03.293 に答える
4

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

それ以外の場合、QObjectを削除すると、最初にすべての信号とスロットが切断され、保留中のすべてのイベントが削除されます。disconnect()を呼び出すと同じようになります。

于 2011-02-03T17:27:34.293 に答える