0

一部の C++ クラス間の結合を減らすために、コールバックを利用してきました。用語を定義するには: コールバックを作成するクラスを呼び出し元と呼び、コールバックを受け取るクラスを呼び出し先と呼びます。通常 (必ずしもそうとは限りません)、呼び出し先は呼び出し元を所有します。設計上、呼び出し元は呼び出し先を認識しません。

呼び出し元オブジェクトの寿命に関連する問題が発生しています。任意のコールバックを行った後も生きているという保証はありません。次の基本的な例を見てください。

void caller::f()
{
    /* Some work */
    if (...)
    {
        /* [1] Execute callback */
        _callee->callback(this);
    }
    /* [2] Some more work */
}

呼び出し先が呼び出し元を動的に割り当て、特定の条件が発生するのを待つためにコールバックに登録したとします。その場合、呼び出し先は [1] のコールバック内から呼び出し元を削除します。その場合、制御は caller::f に戻りthisますが、削除されており、[2] のコードはクラッシュする可能性が高くなります。

一般的なケースでは、呼び出し元は呼び出し先について何も想定できません。this呼び出し先が を所有しているかどうか、または割り当てを解除する可能性があるかどうかがわからないthisため、呼び出し元のメンバー関数のスコープの割り当て解除を防止する一般的な手段が必要になります。

私はそれを使用したことはありませんが、考えられる解決策はboost::shared_ptrsとを中心に展開していると思います。enable_shared_from_thisこれらのコールバックは、処理能力が限られているモバイル デバイスで非常に頻繁に (毎秒 40 回以上) 実行されるため、多くの を作成して渡すオーバーヘッドについても心配していshared_ptrsます。

委任は、Objective-C ではかなり一般的なパターンです。私は、一般的な C++ の設計パターンにはあまり詳しくありません。この問題をすばやく簡単に修正する方法はありますか? そうでない場合、この設計は通常、C++ でどのように行われますか?

4

3 に答える 3

1

先に進み、共有ポインタを使用しますが、可能であればstd::shared_ptr代わりに使用してboost::shared_ptrください。(現在の)標準ライブラリにあるため、不要なブースト依存関係を追加する必要はありません。すでにブーストを使用している場合は、それも問題ありません。

どのような種類のモバイル デバイスについて話しているのかは特定されませんでしたが、最新のスマートフォンのプロセッサは数百または数千メガヘルツで動作し、低電力の電話でさえ Java プログラムを (ガベージ コレクションを使用して) 正常に実行することがよくあります。共有ポインタは基本的に参照カウントされます。これは、リソースを大量に消費するアクティビティではありません。

デバイスが実際に 1 秒間に 40 回以上コールバックを実行できる場合、共有ポインターで問題が発生することはないと思います。実行速度のために時期尚早に最適化しないでください。安全性と健全性のために時期尚早に最適化してください。

于 2012-04-03T15:54:36.857 に答える
1

実行できなくなったコードをバイパスする通常の方法は、例外をスローすることです。これは、呼び出し元を削除した後、通常は呼び出し元に戻る時点で、呼び出し先によって実行される必要があります。例外は、関数の最後の呼び出し元コードでキャッチされます。

私はこの解決策が好きだとは言えませんが、それは呼び出し元を所有する呼び出し先の異常な状況に起因すると思います。

ポインターの 2 番目のコピーを所有する人がいないため、スマート ポインターがどのように役立つかはわかりません。

于 2012-04-03T16:24:25.510 に答える
1

呼び出し先が呼び出し元になるdeleteと、呼び出し元のデストラクタが呼び出されます。が終了したことを確認する必要があるのはそこですf

私はfスレッドだと推測しているので、最も簡単な解決策は次のとおりです。

スレッド:

running = true;
while (!must_exit)
    /* do something */

デスタクタ:

thread->must_exit = true;
while (thread->running)
    sleep(a_little);
/* continue with destruction */

がスレッドでない場合、そのオブジェクト (およびそのデストラクタを通じて) がいつ実行されているか、いつ実行されていないかを知る場合とf同じ原則が適用できます。f


デストラクタ アプローチを使用したくない場合でも、呼び出し先が呼び出す関数を介してこの機能を実装し、f二度と実行せず、停止するまで待機することができます。次に、呼び出し先は呼び出し元の削除を続行します。

だから、このようなもの:

void caller::f()
{
    if (being_deleted)
        return;
    running = true;
    /* Some work */
    if (...)
    {
        /* [1] Execute callback */
        _callee->callback(this);
    }
    /* [2] Some more work */
    running = false;
}

void caller::make_f_stop()
{
    being_deleted = true;
    while (running)
        sleep(a_little);
}
于 2012-04-03T15:46:47.110 に答える