2

3つの異なるクラス(A、B、C)があり、それぞれが共有オブジェクト(オブジェクトX)へのshared_ptrを持っています。私がやりたいのは、オブジェクトXに誰がそれを指しているかについての情報を与えることです。たとえば、それを指すAオブジェクトがない場合、更新時にAクラスによってのみ使用されるデータを計算する必要はありません。また、オブジェクトXは、それを指すCオブジェクトへのポインターのベクトルを保持できるため、Cオブジェクトに関連する一部のデータが更新されたときに、オブジェクトXで関数を呼び出すことができます(ただし、Cオブジェクトは、オブジェクトXから他のデータを照会するためにそれを指します。 )。

私が考えていたのは、ラッパーが構築/割り当て/破棄されるたびにX上の関数へのポインターを呼び出す各クラスのshared_ptrのラッパークラスを作成することです(Xの参照カウントをインクリメント/デクリメントするのと同じ場合) 。コードのアイデアは次のとおりです。

class A
{
public:
    A() : m_ptr(&X::IncrementACount, &X::DecrementACount) { }

    void SetX( const std::shared_ptr<X> &ptr ) { m_ptr = ptr; }
private:
    shared_ptr_wrapper0<X> m_ptr;
};

class B
{
public:
    B() : m_ptr(&X::IncrementBCount, &X::DecrementBCount) { }

    void SetX( const std::shared_ptr<X> &ptr ) { m_ptr = ptr; }
private:
    shared_ptr_wrapper0<X> m_ptr;
};

class C
{
public:
    C() : m_ptr(this, &X::StoreCPointer, &X::RemoveCPointer) { }

    void SetX( const std::shared_ptr<X> &ptr ) { m_ptr = ptr; }
private:
    shared_ptr_wrapper1<X, const C*> m_ptr;
};

AとBの両方に、引数をとらないインクリメント/デクリメント関数が指定されたラップされたスマートポインターがあります。ラップされたスマートポインターが設定/クリアなどされると、それらの関数は、それに含まれるXオブジェクトで呼び出され、A/Bカウンターをインクリメント/デクリメントします。

Cには、(const C *型の)1つの引数を持つラップされたスマートポインターがあります。set / cleard / etcを取得すると、Cのコンストラクター(thisポインター)によって格納されたデータを使用して関数が呼び出され、オブジェクトXのベクトルに追加または削除されます。

だから私の主な質問はこれは良い考えですか?誰がオブジェクトを指しているのかを追跡するというこのアイデアを実現するためのより良い方法はありますか?この種のアイデアの具体的な名前はありますか(私がこのようなものを最初に試したとは想像できませんが、検索しても何も見つからなかったので、正しい名前がわからない可能性があります探す...)

ありがとうございました。

4

3 に答える 3

4

より単純なアプローチは、オブザーバーパターン、別名パブリッシュ/サブスクライバーです。http://en.wikipedia.org/wiki/Observer_patternを参照してください。Xオブジェクトは発行元であり、監視対象の3つの異なるインターフェイスを提供できます。A、B、およびCオブジェクトはサブスクライバーであり、変更の通知を受け取るためにサインアップします。各オブザーバーは、Xのサブスクリプションリストに自分自身を追加および削除する責任があります。Xで何かが変更されると、適切なサブスクリプションリストの全員がインターフェイスを介して通知されます。

于 2012-06-07T02:04:24.197 に答える
0

あなたの問題は、あなたのAオブジェクトを本当に指している唯一のオブジェクトがスマートポインタであるということです。したがって、スマートポインターがコピーされたときにオブジェクトに通知することはできますが(侵入型ポインターを検索)、スマートポインターのコピーを誰が行っているかを知る方法はありません。これは、本当に知りたいことです。

pub/subモデルのようなものが必要です。

以下は、2つの異なるタイプのサブスクライバーを追跡するための疑似コードです。

class A
{
private:
    std::vector<B&> subscriberB;
    std::vector<C&> subscriberC;

public:
    void Subscribe(const B& subscriber) { subscriberB.push_back(subscriber); }
    void Subscribe(const C& subscriber) { subscriberC.push_back(subscriber); }

    // An example of a function where the subscribing objects are notified.
    void SomeFunction()
    {
        // for each subscriber in subscriberB
        subscriber.SomeFunction();

        // for each subscriber in subscriberC
        subscriber.SomeOtherFunction();
    }

};

上記は、サブスクライバーがサブスクライブしているオブジェクトよりも長生きすると仮定して、参照を格納します。そうでない場合は、ベクターにスマートポインターを配置する必要があります。また、同じオブジェクトが2回サブスクライブしないようにするか、サブスクライブを解除するためのメソッドを提供することもできます。

タイプBまたはCのオブジェクトをAに関連付けるように「強制」する場合は、それらのコンストラクターを次のようにすることができます。

// B ctor
B::B(shared_ptr<A> aPtr)
{
    myPointerToA = aPtr;
    myPointerToA ->subscribe(*this);
}

さらに、Bを作成し、それをAに接続するファクトリメソッドを使用することをお勧めthisします。これにより、コンストラクターでの逆参照が回避され、少し臭いがします。

より一般的なアプローチは、Aにサブスクライブしたいクラスが実装する単一タイプのサブスクライバー'InterfaceSubscribeToA'を使用することです。これはあなたが達成しようとしていることに完全に依存します、あなたがあなたの特定の問題を過度に複雑にしている場合に備えて私はそれを言及します!

于 2012-06-07T05:13:56.743 に答える
0

煩わしいリストを使用できます。を作成しclass Base、を作成してA、そのクラスBC継承します。また、最良の手法は、ととを区別するのではなく、すべてのオブザーバーを同じように扱うことです(sとは異なる、特別なアクションを追加した場合はどうCなりますか?)。クラスはそのために最適です。ABDCBase

class Base
{
public:
    virtual void notifyXObjectChanged(/* data you want to pass*/) {}
    void setX(X* obj)
    {
        if(obj != object)
        {
            if(object)
                object->removeFromList(this);
            object = obj;
            if(object)
                object->addToList(this);
        }
    }

private:
    friend class X;
    X* object;
    Base* next;
    Base* previous;
};

そして、Xでは:

class X
{
    // ...
public:
    void addToList(Base* obj)
    {
        if(first == 0 && last == 0)
        {
            // The list is empty
            first = last = obj;
            obj->previous = obj->next = 0;
        }
        else
        {
            // Add the object at the end of the list
            last->next = obj;
            obj->previous = last;
            obj->next = 0;
            last = obj;
        }
    }

    void removeFromList
    {
        // Code for removing an element in a list
    }

    void notify()
    {
        // iterate on the list
    }

private:
    Base* first;
    Base* last;
    // ...
};

利点:

  • 非常に小さなメモリオーバーヘッド
  • とても早い

お役に立てれば

于 2012-06-07T08:53:47.033 に答える