7

オブジェクトが破棄されたときにさまざまなホルダーに通知するために他のオブジェクトで使用するNotifierクラスをC++で作成したいと思います。

template <class Owner>
class Notifier<Owner> {
public:
  Notifier(Owner* owner);
  ~Notifier(); // Notifies the owner that an object is destroyed
};

class Owner;

class Owned {
public:
  Owned(Owner* owner);
private:
  Notifier<Owner> _notifier;
};

私のポイントは、私は密で複雑なオブジェクトグラフを持っているので、所有しているオブジェクトのアドレスを通知機能に保存することを避けたいということです。ノーティファイアクラスを変更して、所有オブジェクトのアドレスをそれ自体のアドレスとコンパイル時に計算されるオフセットから推測できるようにする方法はありますか?

また、オブジェクトは、おそらく同じクラスからの複数の「所有者」に通知する必要がある場合があることにも注意してください。

ありがとう。

4

6 に答える 6

6

GoF Observer Design Patterを見てください。

于 2009-04-02T13:54:53.330 に答える
3

fa.の答えは良いスタートです。ただし、同じタイプの所有者が複数いるという問題は解決しません。解決策の 1 つは、notifier に 1 つの所有者ではなく、所有者のリストを保存させることです。アイデアを示すための簡単な実装を次に示します。

template <typename Owner, typename Owned>
class Notifier
{
  protected:
    Notifier()
    {}

    // Constructor taking a single owner
    Notifier(Owner & o) 
    { 
        owners.push_back(&o); 
    }

    // Constructor taking a range of owners
    template <typename InputIterator>
    Notifier(InputIterator firstOwner, InputIterator lastOwner)
        : owners(firstOwner, lastOwner) {}

    ~Notifier()
    {
        OwnerList::const_iterator it = owners.begin();
        OwnerList::const_iterator end = owners.end();
        for ( ; it != end ; ++it)
        {
            (*it)->notify(static_cast<Owned*>(this));
        }
    }

    // Method for adding a new owner
    void addOwner(Owner & o) 
    { 
        owners.push_back(&o); 
    }

private:
    typedef std::vector<Owner *> OwnerList;
    OwnerList owners;
};

次のように使用できます。

class Owner;

class Owned : public Notifier<Owner, Owned>
{
    typedef Notifier<Owner, Owned> base;

    //Some possible constructors:
    Owned(Owner & o) : base(o) { }

    Owned(Owner & o1, Owner & o2)
    {
        base::addOwner(o1); //qualified call of base::addOwner
        base::addOwner(o2); //in case there are other bases
    }

    Owned(std::list<Owner*> lo) : base(lo.begin(), lo.end()) { }
};

多くの異なるタイプの所有者がいる場合、このソリューションはかなり使いにくくなる可能性があります。この場合、boost メタプログラミング ライブラリ ( MPLFusion ) を参照すると、次のようなことができるコードが得られる可能性があります。

class Owned : public Notifier<Owned, OwnerType1, OwnerType1, OwnerType2>
{
    Owned(OwnerType1 & o1, OwnerType1 & o2, OwnerType2 & o3) 
        : base(o1,o2,o3)
};

ただし、このソリューションの実装には、前のソリューションよりも少し時間がかかります。

于 2009-04-02T15:35:48.807 に答える
3

それは厄介なハックであり、おそらく動作することが保証されていませんが、これはお勧めしません.

次のように説明したようなレイアウトがあるとします。

template <class Owner>
class Notifier<Owner> {
public:
  Notifier(Owner* owner);
  ~Notifier(); // Notifies the owner that an object is destroyed
};

class Owner;

class Owned {
public:
  Owned(Owner* owner);
private:
  Notifier<Owner> _notifier;
};

_notifierがその名前を知っている場合、次Ownedのように のアドレスを計算できます (これはNotifierのコンストラクターで実行されます)。

Owned *p = reinterpret_cast<Owned *>(reinterpret_cast<char *>(this) - offsetof(Owned, _notifier));

基本的に、_notifier は Owned クラス内の固定オフセットにあると想定されています。_notifierしたがって、Owned のアドレスは、のアドレスから同じオフセットを差し引いたものと等しくなります。

繰り返しますが、これは推奨しない未定義の動作ですが、動作する可能性があります。

于 2009-04-02T14:40:16.293 に答える
2

またはこのようなもの:

通知機能から継承し、Owned をテンプレート パラメーターとして追加します。次に、所有するメソッドを notifier 内で使用できるようにします。

template < class Owner , class Owned >
class Notifier
{
public:
    Notifier(Owner* owner)
    {}

    Owned * owned()
    { return static_cast< Owned * >( this ); }

    ~Notifier()
    {
        // notify owner with owned()
    }
};

class Owner
{};

class Owned : public Notifier< Owner , Owned >
{
public:
    Owned( Owner * owner ) : Notifier< Owner , Owned >( owner )
    {}
};
于 2009-04-02T14:44:29.653 に答える
1

解決策の一部は、OwnedをNotifierから継承することです。このように、破壊されたオブジェクトのアドレスは単に「これ」です。

class Owned : public Notifier<Owner> {
public:
  Owned(Owner* owner) 
    : Notifier<Owner>(owner)
  {}
};

しかし、同じクラスの複数の「所有者」を処理するにはどうすればよいでしょうか。「同じクラス」から何回か継承するにはどうすればよいですか?

faの答えのおかげで、これが私が探していた解決策です:

#include <iostream>

template <class Owner, class Owned, int = 0>
class Notifier {
public:
  Notifier(Owner* owner)
    : _owner(owner)
  {}
  ~Notifier() {
    _owner->remove(owned());
  }
  Owned * owned(){ 
    return static_cast< Owned * >( this ); 
  }

private:
  Owner* _owner;
};

class Owner {
public:
  void remove(void* any) {
    std::cout << any << std::endl;
  }
};

class Owned : public Notifier<Owner,Owned,1>, Notifier<Owner,Owned,2> {
public:
  Owned(Owner* owner1, Owner* owner2)
    : Notifier<Owner,Owned,1>(owner1)
    , Notifier<Owner,Owned,2>(owner2)
  {}
};

int main() {
  std::cout << sizeof(Owned) << std::endl;
  Owner owner1;
  Owner owner2;
  Owned owned(&owner1, &owner2);
  std::cout << "Owned:" << (void*)&owned << std::endl << std::endl;
}

ありがとう!

于 2009-04-02T13:48:12.470 に答える
0

私はそれを非常に疑っています。Notifier が構成で使用されたことを知る方法はありません。私がやったらどうしますか

class Foo
{
private:
  Notifier _a, _b, _c;
}

私は間違いを証明したいと思っていますが、Notifier に詳細な情報を明示的に提供しない限り、それが実行可能であるかどうかは疑問です。

于 2009-04-02T13:57:50.483 に答える