2

議論を明確にするために、問題を非常に一般的な方法で説明します。つまり、実際のクラスの名前を提供したり、ドメイン/コンテキストを説明したりしません (ただし、そうであることが判明した場合は、急)。

クラスを想像してみてくださいA。このクラスに 2 つの不変フィールドを持たxyます。さらに、これらとをプライマリフィールドにします。つまり、 /演算子とハッシュ計算関数の実装でのみ使用されます。xy==!=

Aはandに関して不変であるため、 (たとえばand )の複数のインスタンス(つまり、)がそれらのandへのアクセスを暗黙的に共有できるようにすることで、不要な重複がないようにします。xyAa1a2a1.x == a2.x a1.y == a2.ya1 == a2xy

Aさらに、 :に別のフィールドがあると想像してくださいz。これは、セカンダリ可変であり、 の一種の動作調整として機能しますA。設計上、このフィールドをtooの等しいインスタンス間で共有することが望まれます。したがって、この変更を呼び出すと、へのアクセスが共有されているため、影響も受けます。A a1.setZ(...)a2z

その結果、A純粋な値セマンティクスを持つクラスになりますが、そのメンバーは等しいインスタンス間で暗黙的に共有されます。私の知る限り、そのようなパターンはFlyweightまたはaliasingと呼ばれます。

質問に移る前に、もう 1 つ詳細を説明します。プロジェクトのほとんどのクラスは、Pimplイディオムを使用して実装されています。

private:
  class Private;

  Private* p;

クラスAは除外ではありません。そのため、上記のスキームを実装するための提案されたアイデアは次のとおりです。

  1. Pimplのイディオムでは、生のポインターではなく、 共有ポインターを使用します。A::Private
  2. への共有ポインタのグローバル セットを持っていA::Privateます。
  3. のコンストラクターで、適切な共有ポインターがセットに既に存在するAかどうかを確認し(利用して、もちろん)、存在する場合は単にそれに設定 し、そうでない場合は新しいインスタンスを作成してこのセットに共有ポインターを格納 し、同様にそれに設定します。A::PrivatexypA::Privatep
  4. A::Privateのデストラクタは、共有ポインタthisセットから削除する必要があります。

これは、最も簡単で直感的な実装のように見えます。ただし、問題は、このグローバル セットが への共有ポインタを保持しているためA::Private、対応する のすべてのインスタンスAが破棄された場合でも、参照カウンタが に留まり、1に達することがなく0、したがってメモリが解放されないことです。

いくつかの共有ポインターが参照カウンターの下限を設定する方法を提供すると良いと思いました。この場合、たとえば、1到達1するとメモリが解放されるように設定するだけです。残念ながら、一般的なライブラリ (Boost、Qt、Poco など) でそのような動作の実装を見つけられませんでした。もちろん、自分の問題に対して手動で参照カウントを行うこともできますが、それは適切ではなく、車輪の再発明のようなにおいがします。

おそらく、この問題を解決する他の方法があります。あなたの提案を楽しみにしています。

注:この問題を私がよく知っているポインター セマンティクスに変換するようにというアドバイスがあれば、すぐに傍受したいと思います。上記のスキームに正確に対応するソリューションが必要です。

4

2 に答える 2

2

あなたの設計上の問題が何であるかを正しく理解していれば、グローバル セットに弱い、所有していないポインター (例: weak_ptr<>) が含まれるようになります。

std::vector<std::weak_ptr<Private>> _objects;

したがって、オブジェクトへのすべての所有共有ポインターが破棄されると、オブジェクトも同様に破棄されます**。

これで、グローバル セットには dangling が残りますが、そのポインターが生きているオブジェクトを指しているかどうかを確認weak_ptr<>できるのは良いことです( () メンバー関数を使用して、 null の可能性があるオブジェクトを取得します。そうでない場合は ' t、逆参照しません:lockshared_ptr<>

// A simple, hypothetical loop through the collection of objects
// which does something, but checks whether the pointers are
// dangling before doing that something on a possibly dead object
// that would be Undefined Behavior)
std::for_each(_objects.begin(), _objecs.end(), [] (std::weak_ptr<Private> p) 
{
    std::shared_ptr<Private> sp = p.lock();
    if (sp != nullptr)
    {
        sp->callMember(); // For instance...
    }
});

weak_ptr<>オブジェクトが破棄されたときに、オブジェクトに対応する をコレクションから削除する場合は、カスタムの削除ルーチンを使用できます。オブジェクトが破棄されるとルーチンが呼び出され、そのオブジェクトへのポインターが渡されます。この時点で、割り当てを解除する前に、対応する要素をセットから消去できます。

たとえば、型の新しいオブジェクトをインスタンス化しA、それに a を返す関数は、次のshared_ptrようになります。

static std::shared_ptr<object> make_A()
{
    std::shared_ptr<Private> sp(
        new Private(),   // Instantiate the object
        [] (Private* p)  // Set up the custom deleter...
        {
            // Remove the corresponding element from the vector...
            _objects.erase(
                // ...so let's find that element!
                std::find_if( 
                    _objects.begin(),
                    _objects.end(),
                    [p] (std::weak_ptr<priv> wp)
                    {
                        // lock() will return a null pointer if wp is dangling
                        std::shared_ptr<priv> sp = wp.lock();

                        // In case wp is not dangling, return true if and only
                        // if it points to the object we're about to delete
                        return ((sp != nullptr) && (sp.get() == p));
                    })
                );
        });
}

ここでは C++11 を想定していますが、適切に定義されたファンクターでstd::shared_ptr<>with boost::shared_ptr<>std::weak_ptr<>with 、および lambda を置き換えることにより、C++03 でも同じことを簡単に行うことができます。boost::weak_ptr<>

お役に立てれば。

于 2013-03-10T00:03:24.993 に答える
0

Boost.Flyweightを確認しましたか?

于 2013-04-08T14:01:50.717 に答える