1. ホーム
  2. qt

[解決済み] Qtのシグナルとスロットについて、deleteとdeleteLaterはどのように機能するのですか?

2023-07-10 05:19:20

質問

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

ある瞬間に、私は両方のオブジェクトを取り除きたいと思います。もうシグナルも何もありません。消したいのです。 そう思った私は

delete obj1; delete obj2;

しかし、本当にできるのでしょうか? QObjectの仕様にはこうあります。

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

保留中のイベント」とは何でしょうか。 それは、私が私の delete を呼び出している間に、すでに配信されるべき「保留中のイベント」があり、それがクラッシュの原因となる可能性があり、実際に確認することができないということでしょうか?

では、私が呼び出したとしましょう。

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

安全のために

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

どのように解決するのですか?

QObjectの削除は、2つの基本的なルールに従えば、通常は安全です(つまり、通常の実践では、私が知らない病的なケースもあるかもしれません)。

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

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

特に、複雑なシグナル/スロット チェーンがある場合、削除されるオブジェクトからのシグナルまたはイベントによって開始された削除が、いくつかのステップで発生する可能性があるため、どちらも追跡するのが難しい場合があります (POD メンバー変数にアクセス中にクラッシュするなど)。

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