Korrekte Elementreferenzierung bei std::vector


  • Mod

    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!
    

Anmelden zum Antworten