Wie kann ich wissen, die Adresse des Inhabers Objekt in C ++?
-
22-08-2019 - |
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.
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.
Ein Teil der Lösung wäre, erben von Notifier besessen zu haben. Auf diese Weise wird die Adresse des zerstörten Objekts ist einfach ‚this‘ ...
class Owned : public Notifier<Owner> {
public:
Owned(Owner* owner)
: Notifier<Owner>(owner)
{}
};
Aber wie mehrere ‚Besitzer‘ behandeln aus der gleichen Klasse? Wie kann man mehrmals von der ‚gleichen Klasse‘?
erbtDanke href="https://stackoverflow.com/questions/709790/how-can-i-know-the-address-of-owner-object-in-c/709996#709996"> fa des , hier ist die Lösung, die ich suchte:
#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;
}
Danke!
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.