Korrekte Elementreferenzierung bei std::vector
-
Dann brauchst du aber den vector nicht mehr.
-
Sorry, ich habe meinen Verwendungszweck nicht beschrieben.
Es soll in mehreren Bereichen, die kein ownership des Vektors haben, auf die Elemente referenziert werden.
Ich brauche daher weiterhin einen Vektor für die Zeiger, da der Speicher für die Elemente ja zentral verwaltet werden muß.
-
KreuzQuer schrieb:
std::vector<Blah*> testVektor; Blah* elementZeiger = NULL; testVektor.push_back(new Blah(1)); elementZeiger = testVektor.back(); testVektor.push_back(new Blah(2)); // elementZeiger zeigt weiterhin auf das erste Element Blah(1)Da bist Du aber auf den besten Weg Speicherlöcher zu produzieren. In einen Container der Standard Library legt man niemals Zeiger rein, wenn über diese Zeiger die Ownership verwaltet werden muß. Also entweder so
#include <vector> class A {}; int main () { std::size_t const size = 10 A v[size]; std::vector<A*> vec; for (std::size_t i = 0; i != size; ++i) { vec.push_back(&v[i]); } }oder so
#include <vector> #include <boost/shared_pointer.hpp> class A {}; int main () { std::size_t const size = 10; std::vector<boost::shared_pointer<A> > v; for (std::size_t i = 0; i != size; ++i) { v.push_back(boost::shared_pointer<A>(new A)); } }Wenn Du Objekte in den Vektor einfügst und die Kapazität reicht nicht mehr aus, dann werden alle Iteratoren/Zeiger auf Objekte ungültig. Das ist so und kann nicht geändert werden. Da hilft nur dies zu vermeiden bzw. über einen eigenen Memory Allocator die Reallozierung gänzlich zu verhindern. Wenn das alles nicht gangbar ist, solltest Du über ein anderes Design nachdenken.
-
~john:
In deinem Beispiel verstehe ich dann aber wirklich nicht den Sinn des Vektors.
Meine Anwendung von dem Vektor von Zeigern sieht wie folgt aus:
class Typ; class Klasse { private: std:vector<Typ*> typVektor; public: Klasse() {}; Klasse() {for(int i=0; i < typVektor.size(); i++) {delete typVektor[i];} const Typ* PushTypElement(Typ element) {typVektor.push_back(new Typ(element)); return typVektor.back()} };Ich verstehe nicht was daran zu anfällig für Speicherprobleme sein soll.
Danke für die Aufklärung.
-
Wieso wird eigentlich immer gleich zu Pointern gegriffen?
Wenn std::vector ungeeignet ist, weil er ggf. keine stabilen Referenzen liefert, dann benutz einen anderen Container. std::deque dürfte für den skizzierten Fall geeignet sein.
achja:KreuzQuer schrieb:
Das währe recht unbequem da ja die oft ohne vector-ownership zu einzelnen Elementen referenziert werden muß.
Die erste Satzhälfte verstehe ich ja noch, den Rest nicht...
-
camper schrieb:
Wieso wird eigentlich immer gleich zu Pointern gegriffen?
Wenn std::vector ungeeignet ist, weil er ggf. keine stabilen Referenzen liefert, dann benutz einen anderen Container. std::deque dürfte für den skizzierten Fall geeignet sein.Ich verstehe nicht warum std::deque dafür geeigneter sein soll.
achja:
KreuzQuer schrieb:
Das währe recht unbequem da ja die oft ohne vector-ownership zu einzelnen Elementen referenziert werden muß.
Die erste Satzhälfte verstehe ich ja noch, den Rest nicht...
Gemeint war, dass es aufwändiger ist z.B. über den Indexwert eines Elementes auf ein Element zuzugreifen (Funktionsaufruf von außerhalb notwendig), als direkt eine Referenz (im Beispiel Zeiger auf konstantes Objekt) zum Element zu besitzen.
-
Du könntest auch die eigentlichen Objekte in einem Container wie list (oder deque -- deque invalidiert Iteratoren glaub ich nur beim Einfügen in der Mitte, bin aber nicht sicher) lagern, und in einem vector Pointer darauf verwalten.
-
KreuzQuer schrieb:
~john:
In deinem Beispiel verstehe ich dann aber wirklich nicht den Sinn des Vektors.
Das Array dient nur dazu Elemente für den Vektor bereitzustellen, es hätte auch irgend welche anderen Objekte sein können. So ist das nur kompakter zu schreiben.
Meine Anwendung von dem Vektor von Zeigern sieht wie folgt aus:
class Typ; class Klasse { private: std:vector<Typ*> typVektor; public: Klasse() {}; Klasse() {for(int i=0; i < typVektor.size(); i++) {delete typVektor[i];} const Typ* PushTypElement(Typ element) {typVektor.push_back(new Typ(element)); return typVektor.back()} };Ich verstehe nicht was daran zu anfällig für Speicherprobleme sein soll.
Danke für die Aufklärung.Denk mal darüber nach was passiert, wenn ein Objekt der Klasse "Klasse" destruiert wird? Wie sieht es mit dem Kopierzuweisungsoperator aus? ...
Alle Elemente im Vektor typVektor werden destruiert, da es sich um PODs (einfache Zeiger auf Typ) handelt wird gar nichts gemacht, und der Speicher für die referenzierte Objekte vom Typ "Typ" wird nicht freigegeben und die Destruktoren für die Objekte werden nicht aufgerufen.
-
Um das Problem zu verdeutlichen
class Typ; class Klasse { private: std:vector<Typ*> typVektor; public: Klasse() {}; ~Klasse() {for(int i=0; i < typVektor.size(); i++) {delete typVektor[i];} const Typ* PushTypElement(Typ element) {typVektor.push_back(new Typ(element)); return typVektor.back()} }; int main () { Klasse A; // Code zum Füllen von A; { Klasse B = A; } // ab hier enthält A nur noch Zeiger auf Datenmüll! } // hier versucht der Destruktor bereits freigegeben Objekte freizugeben!
-
~john schrieb:
Denk mal darüber nach was passiert, wenn ein Objekt der Klasse "Klasse" destruiert wird?
Alle Elemente im Vektor typVektor werden destruiert, da es sich um PODs (einfache Zeiger auf Typ) handelt wird gar nichts gemacht, und der Speicher für die referenzierte Objekte vom Typ "Typ" wird nicht freigegeben und die Destruktoren für die Objekte werden nicht aufgerufen.Ich hatte mich lediglich vertippt - der Destruktur ist natürlich definiert:
class Typ; class Klasse { private: std:vector<Typ*> typVektor; public: Klasse() {}; ~Klasse() {for(int i=0; i < typVektor.size(); i++) {delete typVektor[i]};} const Typ* PushTypElement(Typ element) {typVektor.push_back(new Typ(element)); return typVektor.back();} };Da wird der Speicher korrekt freigegen, und niemand als das Objekt selbst hat schreibenden Zugriff auf die Elemente hinter dem Zeiger.
Die Objekte, die eine Referenz zu den Elementen besitzen, werden stets vor dem Objekt mit dem Vektor selbst vernichtet.
-
KreuzQuer schrieb:
Da wird der Speicher korrekt freigegen, und niemand als das Objekt selbst hat schreibenden Zugriff auf die Elemente hinter dem Zeiger.
Schau Dir das Posting vorher an.
-
~john schrieb:
Um das Problem zu verdeutlichen
int main () { Klasse A; // Code zum Füllen von A; { Klasse B = A; } // ab hier enthält A nur noch Zeiger auf Datenmüll! } // hier versucht der Destruktor bereits freigegeben Objekte freizugeben!Das ist doch Sache des Copy-Ctor, der natürlich korrekt definiert sein muß wenn man ihn verwenden will.
Nach dieser Argumentation dürfte man ja gar keine Zeiger als Objektvariablen verwenden.
-
Es geht um Kontrakte. Alle Container der Standard Library sind auf Speicherung von Werten und nicht von Zeiger auf Objekte ausgelegt. Daher empfiehlt es sich nur Objekte direkt oder SmartPointer auf Objekte in einem Container der Standard Library abzulegen, da man so weniger Arbeit hat und nichts übersieht.
class Typ; class Klasse { std:vector<boost::shared_ptr<Typ> > typVektor; public: Typ const* PushTypElement(Typ element) { boost::shared_ptr<Typ> sp (new Typ(element)); typVektor.push_back(sp); return sp.get(); } };Alternativ kann man sich natürlich auch direkt die SmartPointer zurückliefern lassen.
-
KreuzQuer schrieb:
~john schrieb:
Um das Problem zu verdeutlichen
int main () { Klasse A; // Code zum Füllen von A; { Klasse B = A; } // ab hier enthält A nur noch Zeiger auf Datenmüll! } // hier versucht der Destruktor bereits freigegeben Objekte freizugeben!Das ist doch Sache des Copy-Ctor, der natürlich korrekt definiert sein muß wenn man ihn verwenden will.
Nach dieser Argumentation dürfte man ja gar keine Zeiger als Objektvariablen verwenden.ich klink mich hier mal ein, ggf. ist der punkt hier nicht ganz klar
int main () { Klasse A; // Code zum Füllen von A; { Klasse B = A; //hier zeigen die zeiger aus A und B auf die gleichen Obejekte! } //hier wird der dtor von B aufgerufen, was dazu führt das die Objekte zerstört werden // ab hier enthält A nur noch Zeiger auf Datenmüll! weil dotr von B sie //zerstört hat !! } // hier versucht der Destruktor von A bereits freigegeben Objekte freizugeben!