Hallo, ich habe heute eine Frage zum Einfügen verschiedener Objekttypen in dasselbe Vektorarray gestellt. Mein Code in dieser Frage war
gate* G[1000];
G[0] = new ANDgate() ;
G[1] = new ORgate;
//gate is a class inherited by ANDgate and ORgate classes
class gate
{
.....
......
virtual void Run()
{ //A virtual function
}
};
class ANDgate :public gate
{.....
.......
void Run()
{
//AND version of Run
}
};
class ORgate :public gate
{.....
.......
void Run()
{
//OR version of Run
}
};
//Running the simulator using overloading concept
for(...;...;..)
{
G[i]->Run() ; //will run perfectly the right Run for the right Gate type
}
und ich wollte Vektoren verwenden, also schrieb jemand, dass ich das tun sollte:
std::vector<gate*> G;
G.push_back(new ANDgate);
G.push_back(new ORgate);
for(unsigned i=0;i<G.size();++i)
{
G[i]->Run();
}
aber dann schlugen er und viele andere vor, dass ich besser Boost-Zeigercontainer
oder verwenden sollte shared_ptr
. Ich habe die letzten 3 Stunden damit verbracht, über dieses Thema zu lesen, aber die Dokumentation scheint mir ziemlich fortgeschritten zu sein. **** Kann mir jemand ein kleines Codebeispiel für die shared_ptr
Verwendung geben und warum sie die Verwendung vorgeschlagen haben shared_ptr
. Auch gibt es andere Typen wie ptr_vector
, ptr_list
und ptr_deque
** **
Edit1: Ich habe auch ein Codebeispiel gelesen, das Folgendes enthielt:
typedef boost::shared_ptr<Foo> FooPtr;
.......
int main()
{
std::vector<FooPtr> foo_vector;
........
FooPtr foo_ptr( new Foo( 2 ) );
foo_vector.push_back( foo_ptr );
...........
}
Und ich verstehe die Syntax nicht!
quelle
main
erzeugt einen Vektor, der genannten Shared - Zeiger auf einen Typ enthalten kannFoo
; Der zweite erstellt eineFoo
Verwendungnew
und einen gemeinsamen Zeiger, um sie zu verwalten. Der dritte fügt eine Kopie des gemeinsam genutzten Zeigers in den Vektor ein.Antworten:
Durch die Verwendung
vector
von ashared_ptr
wird die Möglichkeit eines Speicherverlusts beseitigt, da Sie vergessen haben, den Vektor zu durchlaufen unddelete
jedes Element aufzurufen . Lassen Sie uns eine leicht modifizierte Version des Beispiels Zeile für Zeile durchgehen.Erstellen Sie einen Alias für den gemeinsam genutzten Zeigertyp. Dies vermeidet die Hässlichkeit in der C ++ - Sprache, die sich aus der Eingabe
std::vector<boost::shared_ptr<gate> >
und dem Vergessen des Leerzeichens zwischen den schließenden Größer-als-Zeichen ergibt .Erstellt einen leeren Vektor von
boost::shared_ptr<gate>
Objekten.Ordnen Sie eine neue
ANDgate
Instanz zu und speichern Sie sie in einershared_ptr
. Der Grund dafür ist, ein Problem zu vermeiden, das auftreten kann, wenn eine Operation ausgelöst wird. Dies ist in diesem Beispiel nicht möglich. Die Boost -shared_ptr
„Best Practices“ erklären , warum es dich um eine Best - Practice in ein freistehendes Objekt zuweisen anstelle einem zeitlich begrenzt.Dadurch wird ein neuer gemeinsamer Zeiger im Vektor erstellt und
ptr
in diesen kopiert . Die Referenzzählung in den Eingeweiden vonshared_ptr
stellt sicher, dass das zugewiesene Objekt innerhalb vonptr
sicher in den Vektor übertragen wird.Was nicht erklärt wird, ist, dass der Destruktor für
shared_ptr<gate>
sicherstellt, dass der zugewiesene Speicher gelöscht wird. Hier wird der Speicherverlust vermieden. Der Destruktor fürstd::vector<T>
stellt sicher, dass der Destruktor fürT
für jedes im Vektor gespeicherte Element aufgerufen wird. Der Destruktor für einen Zeiger (z. B.gate*
) löscht jedoch nicht den von Ihnen zugewiesenen Speicher . Das versuchen Sie mitshared_ptr
oder zu vermeidenptr_vector
.quelle
int x(5);
umx
mit dem Wert 5 zu initialisieren . In diesem Fall wird er mit dem Wert des neuen Ausdrucks initialisiert, der einANDgate
; Der Wert des neuen Ausdrucks ist ein Zeiger auf das neue Objekt.Ich werde hinzufügen, dass eines der wichtigsten Dinge bei
shared_ptr
's ist, sie immer nur mit der folgenden Syntax zu konstruieren:Auf diese Weise ist der "echte" Zeiger auf
Type
für Ihren Bereich anonym und wird nur vom gemeinsam genutzten Zeiger gehalten. Daher können Sie diesen "echten" Zeiger nicht versehentlich verwenden. Mit anderen Worten, tun Sie dies niemals:Obwohl dies funktioniert, haben Sie jetzt einen
Type*
Zeiger (t_ptr
) in Ihrer Funktion, der sich außerhalb des gemeinsam genutzten Zeigers befindet. Es ist gefährlich, est_ptr
überall zu verwenden , da Sie nie wissen, wann der gemeinsam genutzte Zeiger, der es enthält, es zerstören kann, und Sie werden einen Fehler machen.Gleiches gilt für Zeiger, die Ihnen von anderen Klassen zurückgegeben wurden. Wenn eine Klasse, die Sie nicht geschrieben haben, Ihnen einen Zeiger gibt, ist es im Allgemeinen nicht sicher, ihn einfach in eine zu setzen
shared_ptr
. Nur wenn Sie sicher sind , dass die Klasse dieses Objekt nicht mehr verwendet. Wenn Sie es in ashared_ptr
einfügen und es außerhalb des Gültigkeitsbereichs liegt, wird das Objekt freigegeben, wenn die Klasse es möglicherweise noch benötigt.quelle
auto t_ptr = make_shared<Type>(...);
oder gleichwertigshared_ptr<Type> t_ptr = make_shared<Type>(...);
, einfach weil diese Form effizienter ist.,
zwischent_sptr
undptrT
in gebenshared_ptr<Type> t_sptr ptrT(t_ptr);
?Das Erlernen des Gebrauchs intelligenter Zeiger ist meiner Meinung nach einer der wichtigsten Schritte, um ein kompetenter C ++ - Programmierer zu werden. Wie Sie wissen, möchten Sie ein Objekt immer dann löschen, wenn Sie es neu erstellen.
Ein Problem ist, dass es mit Ausnahmen sehr schwierig sein kann, sicherzustellen, dass ein Objekt in allen möglichen Ausführungspfaden immer nur einmal freigegeben wird.
Dies ist der Grund für RAII: http://en.wikipedia.org/wiki/RAII
Erstellen einer Hilfsklasse mit dem Ziel, sicherzustellen, dass ein Objekt in allen Ausführungspfaden immer einmal gelöscht wird.
Ein Beispiel für eine Klasse wie diese ist: std :: auto_ptr
Aber manchmal möchten Sie Objekte mit anderen teilen. Es sollte nur gelöscht werden, wenn es von niemandem mehr verwendet wird.
Um dies zu unterstützen, wurden Referenzzählstrategien entwickelt, aber Sie müssen sich immer noch an addref erinnern und ref manuell freigeben. Im Wesentlichen ist dies das gleiche Problem wie neu / löschen.
Aus diesem Grund hat boost boost :: shared_ptr entwickelt, einen intelligenten Zeiger für die Referenzzählung, mit dem Sie Objekte gemeinsam nutzen und nicht ungewollt Speicher verlieren können.
Mit der Hinzufügung von C ++ tr1 wird dies nun auch zum c ++ - Standard hinzugefügt, jedoch mit dem Namen std :: tr1 :: shared_ptr <>.
Ich empfehle, wenn möglich den gemeinsamen Standardzeiger zu verwenden. ptr_list, ptr_dequeue usw. sind IIRC-spezialisierte Container für Zeigertypen. Ich ignoriere sie jetzt.
Wir können also von Ihrem Beispiel ausgehen:
Das Problem hierbei ist nun, dass jedes Mal, wenn G den Gültigkeitsbereich verlässt, die 2 zu G hinzugefügten Objekte verloren gehen. Schreiben wir es neu, um std :: tr1 :: shared_ptr zu verwenden
Wenn G den Gültigkeitsbereich verlässt, wird der Speicher automatisch zurückgefordert.
Eine Übung, mit der ich Neulinge in meinem Team plagte, ist, sie zu bitten, ihre eigene Smart-Pointer-Klasse zu schreiben. Wenn Sie fertig sind, verwerfen Sie die Klasse sofort und verwenden Sie sie nie wieder. Hoffentlich haben Sie wichtige Kenntnisse darüber erworben, wie ein intelligenter Zeiger unter der Haube funktioniert. Es gibt wirklich keine Magie.
quelle
for( auto itt = G.begin(); itt != G.end(); ++itt ){ itt->Run(); }
Die Boost-Dokumentation bietet ein ziemlich gutes Startbeispiel : shared_ptr-Beispiel (es handelt sich tatsächlich um einen Vektor von intelligenten Zeigern) oder shared_ptr doc Die folgende Antwort von Johannes Schaub erklärt die Boost-intelligenten Zeiger ziemlich gut: intelligente Zeiger erklärt
Die Idee hinter (in so wenigen Worten wie möglich) ptr_vector ist, dass es die Freigabe des Speichers hinter den gespeicherten Zeigern für Sie übernimmt: Nehmen wir an, Sie haben einen Zeigervektor wie in Ihrem Beispiel. Wenn Sie die Anwendung beenden oder den Bereich verlassen, in dem der Vektor definiert ist, müssen Sie nach sich selbst bereinigen (Sie haben ANDgate und ORgate dynamisch zugewiesen), aber nur das Löschen des Vektors reicht nicht aus, da der Vektor die Zeiger speichert und nicht die tatsächlichen Objekte (es wird nicht zerstören, sondern was es enthält).
boost :: ptr_vector <> erledigt die oben genannten Aufgaben für Sie. Dies bedeutet, dass der Speicher hinter den gespeicherten Zeigern freigegeben wird.
quelle
Durch Boost können Sie es tun>
> zum Einfügen eines anderen Objekttyps in Ihren Vektorcontainer. Während Sie für den Zugriff any_cast verwenden müssen, das wie dynamic_cast funktioniert, hofft es, dass es für Ihre Anforderungen funktioniert.
quelle
Dies ist ein Beispiel für shared_ptr in Aktion. _obj2 wurde gelöscht, aber der Zeiger ist noch gültig. Ausgabe ist ./test _obj1: 10 _obj2: 10 _obj2: 10 erledigt
quelle
Der beste Weg, um verschiedene Objekte in denselben Container einzufügen, ist die Verwendung der Schleife make_shared, vector und range. Sie erhalten einen schönen, sauberen und "lesbaren" Code!
quelle