Frage

Ich möchte in C ++ eine Notifier-Klasse erstellen, die ich in anderen Objekten verwenden verschiedene Halter zu benachrichtigen, wenn das Objekt zerstört wird.

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

Mein Punkt ist, dass ich ein dichtes und komplizierte Objektgraphen habe, würde ich in dem Anmelder die Adresse des im Besitz Objekts vermeiden möge zu speichern. Gibt es eine Möglichkeit, meine Notifier-Klasse zu ändern, so dass es das im Besitz des Objekts Adresse aus seiner eigenen Adresse ableiten kann, und ein Offset, dass zum Zeitpunkt der Kompilierung berechnet werden würde?

Beachten Sie auch, dass jedes Objekt müssen mehrere ‚Eigentümer‘, möglicherweise aus der gleichen Klasse unterrichten kann.

Danke.

War es hilfreich?

Lösung

Oder etwas wie folgt aus:

von Ihrem Notifier Inherit und fügen Sie als Template-Parameter gehört. Dann können Sie eine Methode zur Verfügung, im Besitz innerhalb der Anmelder haben:

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

Andere Tipps

Schauen Sie sich die GoF Observer Design-Patter .

Es wäre ein böse sein hacken und wahrscheinlich nicht garantiert werden, aber hier ist ein Gedanke ich nicht empfehlen diese .

Angenommen, Sie haben Ihr Layout wie Sie wie folgt beschrieben:

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

Wenn _notifier seinen Namen kennt, könnte es Owned Adresse wie folgt berechnen (die in der Notifier Konstruktor ausgeführt wird):

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

im Grunde ist die Annahme, dass _notifier ist bei einigen innerhalb der Owned Klasse fester Offset. Deshalb ist die Adresse der Besitzer gleich Adresse minus der _notifier dass gleiche Offset.

Wieder einmal ist dies nicht definiertes Verhalten, das würde ich nicht empfehlen, könnte aber möglicherweise funktionieren.

fa.‘ s Antwort ist ein guter Anfang. Allerdings löst es nicht das Problem von mehreren Eigentümern des gleichen Typs. Eine Lösung ist, den Notifier Speicher eine Liste der Eigentümer zu haben, statt eines einzigen. Hier ist eine schnelle Umsetzung, um die Idee zu zeigen:

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

Sie können es auf diese Weise verwendet werden:

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

In dem Fall, dass Sie viele verschiedene Arten von Eigentümer haben, kann diese Lösung recht schwierig geworden, zu verwenden. In diesem Fall sollten Sie bei den Boost-metaprogramming Bibliotheken suchen ( MPL , Fusion ), mit dem kann man mit einem Code am Ende, die Sie tun stopft so lassen:

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

Allerdings Implementierung dieser Lösung wäre ein wenig länger als die vorherige.

Ich bezweifle es stark. Es gibt keine Möglichkeit für den Notifier zu wissen, dass es in der Zusammensetzung verwendet wurde. Was passiert, wenn ich

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

Ich würde gerne obwohl falsch bewiesen werden, aber ich bezweifle wirklich, ohne explizit zu geben weitere Informationen zu den Notifier machbar.

scroll top