2009-04-02 6 views
7

私はC++でNotifierクラスを作成したいと思います。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; 
}; 

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

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

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

+0

静的多型が必要か、抽象基底クラスIOwnerを例えば以下のように作成できますか?純粋な仮想メソッド 'notify'? –

+0

いいえ、私は純粋な仮想 'notify'を持つことはできません。 –

答えて

2

または次のようなもの:

所有者を継承し、テンプレートパラメータとして所有します。次に、通知内の利用可能所有するメソッドを持つことができます。

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) 
    {} 
}; 
+0

+1。それは私の意見では最高の解決策です。ただし、同じタイプの複数の所有者は許可されません。 –

+0

@Luc:そうですね、同じタイプの複数のオーナーがちょっとした作業をするだけです。 intテンプレートパラメータ...私の答えを見てください。 –

+0

このソリューションは、オブジェクトグラフがほぼ静的である(同じ種類のオブジェクトが2つ以上ない)場合にのみ適用されます。ツリーのようにインスタンスを動的に作成する場合は、所有者のインスタンスを保存する必要があります。 – mmmmmmmm

1

解決策の一部は、所有者が通知機能を継承していることです。この方法では、破壊されたオブジェクトのアドレスは単純に 'this'です...

同じクラスから複数のオーナーを処理するにはどうすればよいですか?どのようにして、同じクラスから数回継承できますか? fa's answer

おかげで、ここで私が探していたソリューションです:

#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; 
} 

ありがとう!

+0

通知者には、1人ではなく所有者のリストを保存できませんか? –

+0

あなたのユースケースでは、コンパイル時にこれをすべて知ることができますか?また、非常に多くのテンプレートをインスタンス化するコードが肥大化しているので大丈夫ですか? – rmeador

+0

ユースケースは、ビジネスオブジェクトモデルのクラス間の関係を実装することです。例えば。需要は顧客と結びついています。私はそれらのクラスについてすべてを知っています。各需要には正確に1人の顧客がおり、各顧客には0からnの需要があることがわかります... –

6

GoF Observer Design Patterをご覧ください。

+0

私は実際に1対多の関係。 Observerパターンにはうまく使用できますが、必ずしも必要ではありません。 –

+0

@Xavier:私はあなたを理解しているか分からない。 "オブザーバーのために非常によく使われる"。オブザーバーは、通知を実装する方法です。 –

+0

"Observerは1対多の関係を定義しているため、あるオブジェクトの状態が変更されたときに他のオブジェクトが自動的に通知および更新されるようになります。しかし、私は多くの人にその旨を(メモリ効率のよい方法で)知らせてほしい。 –

0

私はそれを非常に疑っています。 Notifierは、それが合成で使用されたことを知る方法がありません。私は

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

を行う場合でも、私は間違って証明するのが大好きですが、私は実際にそれが明示的に通知へのより多くの情報を与えることなくなんとかだ疑い。

+0

コンパイル時にこの情報を与えるためのテンプレート(または他の)トリックですか? –

+0

テンプレートがあなたを助ける方法はありません。 –

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がその名前を知っている場合、それは(コンストラクタOwnedNotifierで実行される)。このようなsのアドレス」を計算することができます:

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

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

もう一度、これは私がお勧めしないが動作する可能性のある未定義の動作です。

3

fa.'s answerは良いスタートです。ただし、同じタイプの複数の所有者がいるという問題は解決しません。 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()) { } 
}; 

あなたが所有者の多くの異なる種類を持っている場合は、このソリューションはかなり困難になることができます使用する。この場合、あなたはあなたがそのような原料をやらせるコードで終わる可能性があるとブーストメタプログラミングライブラリ(MPLFusion)、見たいかもしれません:

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

しかし、このソリューションを実装するだろう以前のものより少し長くなります。

+0

これは、所有者の動的リストが必要な場合には非常に興味深いです。私の場合、各タイプの所有者数は事前に分かっています(通常は1つ、場合によっては2つ、それ以上は決してありません)。 –

関連する問題