instanzierung wie Array hanhaben = Problem



  • Guck dir mal std::vector an, das ist eine Art dynamisches Array, dessen Grösse du zur Laufzeit ändern kannst.



  • Ist nicht wass ich suche, NoobLolo.
    Könntest du mir ein Tip geben DocShoe, wie das funktioniert? Habe bis jetzt kaum mit Vectoren gearbeitet.

    mein aktueller Stand:
    hpp:

    #include "Zwei.hpp"
    
    #include <vector>
    #include <iostream>
    
    using namespace std;
    class C_Eins
    {
      private:
       vector<C_Zwei>cv_vec;
       void tu_was();
    };
    

    cpp:

    #include "Eins.hpp"
    
    void C_Eins::tu_was()
    {
       int layer = 2;
       cv_vec.push_back(layer);
    
        cv_vec[0].funktion("werte angeben");
        cv_vec[1].funktion("werte angeben");
    
        cv_vec[0].var = 5;
        cv_vec[1].var = 7;
    }
    

    Irgendwie giebts bei "cv_vec.push_back(layer);" ein Compiler fehler. Nehme an, dass dies so nicht geht.



  • Klar. Dein vector enthält Objekte vom Typ C_Zwei, du versuchst einen int in den vector einzufügen, was natürlich nicht geht.
    Tipps zur generellen Benutzung:
    Ein vector ist anfangs leer, über push_back() können zur Laufzeit weitere Elemente hinzugefügt werden. Wenn du im Vorfeld schon weisst, wieviele Elemente der vector enthalten soll kannst du entweder resize() aufrufen, um entsprechend viele Elemente im vector anzulegen, oder du rufst reserve()/push_back() auf, um die Elemente hinzuzufügen.
    Folgender Code erzeugt vektoren mit jeweils 2 Elementen:

    #include <vector>
    
    int main()
    {
       // 1. Anzahl der Elemente wird im Konstruktor übergeben
       std::vector<int> v1( 2 );
    
       // 2. Grössenänderung per resize()
       std::vector<int> v2;
       v2.resize( 2 );
    
       // 3. Füllung per reserve()/push_back()
       std::vector<int> v3;
       v3.reserve( 2 );
       v3.push_back( 1 );
       v3.push_back( 2 );
    }
    

    Der Aufruf von reserve() ist nicht zwingend erforderlich, kann aber die Laufzeit verbessern. Ein vector verwaltet einen internen Datenblock, dessen Grösse er üblicherweise verdoppelt, wenn nicht mehr genügend Platz vorhanden ist, um ein neues Element aufzunehmen. Wenn er neuen Speicher anfordert müssen die bestehenden Objekte in den neuen Speicher kopiert werden, was natürlich Zeit kostet. Um diesen Kopieraufwand zu vermeiden teilt man dem vector mit reserve() vorher mit, wieviele Objekte man speichern möchte, sodass vector genügend Speicher anfordern kann, um alle Elemente ohne Reallokation zu speichern.



  • Lumo schrieb:

    mein aktueller Stand:

    1. Bitte verwende statt den code-Tags die cpp-Tags (Für farbige hervorhebung der Schlüsselwörter).
    2. Verwende "using namespace" möglichst niemals in Codeeinheiten die selbst wieder inkludiert werden (Header...). Das "entleert" einen Namensraum global, und führt ihn ab absurdum.
    3. Auch in Beispielen sollte man möglichst sinnvolle Bezeichner wählen.
    4. Ein Vektor füllt man mit Elementen des entsprechenden Typs, bei dir Objekte vom Typ "C_Zwei", nicht int (Du versuchst die Integervariable layer einzufügen).

    Mal ein anderes Beispiel (ungetestet)

    // Garage.h
    #ifndef __GARAGE__HEADER__
    #define __GARAGE__HEADER__
    
    #include <vector>
    #include <iostream>
    #include "Auto.hpp"
    
    class Garage
    {
      //...
      public:
          void Einparken(Auto const & auto);
      private:
          std::vector<Auto> autos;
          void hubKonzert();
    };
    
    #endif
    
    // Garage.cpp
    #include "Garage.h"
    
    void Garage::Einparken(Auto const & auto)
    {
        // Ein Auto in den Vector einfügen
        autos.push_back(auto);
    }
    
    void Garage::hubKonzert()
    {
        // Nun in jedem Auto die Hube bedienen
        // Einmal über den Index, einmal über Iteratoren
    
        // a) Über Größe und Index
        for(int position=0, anzahlAutos=autos.size(); position<anzahlAutos;
          ++position)
            autos[position].Hupen();
    
        // b) Über Iteratoren
        for(std::vector<Auto>::const_iterator it, end=autos.end(); it!=end; ++it)
            it->Hupen();
    }
    


  • Ich danke dir für dein Post! Ich habe in zwischen noch ein Tutorial gefunden. Es funktioniert jetzt. Aber ich hätte da noch eine frage.

    hier ein paar code ausschnitte:
    in der hpp:

    private:
            //Anzahl an Bildern
            int m_layer;
            //Vector für die Bilder
            vector<C_PNGFromFile*> cv_picture;
    

    in der cpp:

    //Hier wird die Anzahl an Bildern abgeholt
        int m_layer = D_Backgrounds::dm_layer;
    
        //Speicher wird reserviert
        cv_picture.reserve( m_layer );
        //Für jedes Bild und seine Daten wird eine neu
        //Instanz in den Vector geschrieben.
        for(int i = 0; i<=m_layer; i++)
        cv_picture.push_back(new C_PNGFromFile);
    

    Sieht dies in deinen Augen gut aus? Und im Vector sind jetzt ja Pointers, die ich mit "new" reserviert habe. Ich nehme an ich muss die wieder löschen. Wenn ja, wie lösche ich sie? Ich will hier einfach kein Fehler machen!!



  • Ahjo, da kommen wir zur nächsten Baustelle 😉
    Da du die Elemente im vector per new anlegst musst du sie natürlich auch wieder mit delete zerstören. Dazu gibt es drei Lösungen:

    1. jedesmal, wenn du ein Element aus dem vector löscht musst du den Speicher mit delete wieder freigeben. Bevor der vector zerstört wird (z.B. wenn er out of scope geht oder das Programm beendet wird) musst du das für alle verbleibenden Objekte im vector ebenfalls machen.

    2. Wenn du Zugriff auf die boost Bibliotheken hast kannst du die Klasse boost::ptr_vector verwenden. Sie müsste das gleiche Interface wie std::vector besitzen und dir die Resourcenverwaltung abnehmen

    3. Wenn du Zugriff auf die boost Bibliotheken oder einen Compiler mit TR1 Unterstützung hast kannst du deine dynamischen Objekte in shared_ptr verkapseln. shared_ptr übernimmt ebenfalls die komplette Resourcenverwaltung für dich.

    Lösung 3 ist mit Sicherheit die Beste, wenn du allerdings die boost Bibliotheken/TR1 nicht benutzen kannst musst du wohl auf Lösung 1
    zurückgreifen. Oder deinen eigenen Smart Pointer programmieren.

    PS:
    Ja, sieht gut aus



  • Lumo schrieb:

    Sieht dies in deinen Augen gut aus? Und im Vector sind jetzt ja Pointers, die ich mit "new" reserviert habe. Ich nehme an ich muss die wieder löschen. Wenn ja, wie lösche ich sie? Ich will hier einfach kein Fehler machen!!

    Die Frage ist eher, ob das Zeiger sein müssen in dem vector.

    Falls du wirklich Zeiger drin hast, dann kannst du die über den Iterator löschen. z.B um alle Elemente aus einem std::vector zu löschen:

    typedef std::vector<C_PNGFromFile*> container_type;
    for (container_type::iterator it = cv_picture.begin (), end = cv_picture.end (); it != end;)
    {
      delete *it; // objekt zerstören
      it = cv_picture.erase ( it ); // und es aus dem vector entfernen
    }
    

    Wie DocShoe richtig andeutet gibt es aber auch andere Möglichkeiten, welche wohl eher passen, als da selbst zu hantieren. (z.B boost::ptr_vector, der genau für so Sachen da ist).

    @DocShoe
    Lösung 3 ist nicht die beste. Mal ganz davon abgesehen, dass der Begriff "beste" nicht viel Sinn macht, da es einfach von den Anforderungen abhängt, gibt es ja einen Grund, warum boost ptr_vector anbietet.. shared_ptr bringen Overhead mit, den man nicht bezahlen sollte, wenn man keinen verteilten Pointer haben möchte.



  • Die Frage ist eher, ob das Zeiger sein müssen in dem vector.

    Von miraus lieber nicht, allerdings wahr dies Zufall, dass ich es hinbekommen habe. Daher, ich habe keine Ahnung wie ich es ohne Pointer hinkriege. Ich habs schon Probier, allerdings ohne Erfolg.

    Wenn ich es nicht hinkriege, ohne Pointer zu arbeiten müsste ich mir überlegen, ob ich alles von Hand lösche, oder ob ich alles mi der "boost Bibliothek" mache. Weiss ich noch nicht!



  • Lumo schrieb:

    Die Frage ist eher, ob das Zeiger sein müssen in dem vector.

    Von miraus lieber nicht, allerdings wahr dies Zufall, dass ich es hinbekommen habe. Daher, ich habe keine Ahnung wie ich es ohne Pointer hinkriege. Ich habs schon Probier, allerdings ohne Erfolg.

    Wenn ich es nicht hinkriege, ohne Pointer zu arbeiten müsste ich mir überlegen, ob ich alles von Hand lösche, oder ob ich alles mi der "boost Bibliothek" mache. Weiss ich noch nicht!

    Kann schon sein, dass es ohne Zeiger nicht geht. Vor allem, wenn man Sachen hat, die nicht Kopierbar sind. Dann ist das (zumindest mit dem jetztigen Stand) eher problematisch. Und irgendwie sieht die Klasse so aus, als ob ein kopieren nicht gescheit wäre. (meine Vermutung. ;))



  • Also bei Grafiken machen Zeiger in Containern schon Sinn, da die Objekte riesig sein können sodass Kopien vermieden werden sollten.

    Ich bin bei der Bewertung von meinen bisherigen Designansätzen ausgegangen, wo ich shared_ptr zurückgebe. Damit ist klar, dass ich mich später nicht um Aufräumarbeiten kümmern muss. Sollte eine Funktion/Methode einen Rohzeiger zurückgeben stelle ich mir immer die Frage, wer dafür verantwortlich ist. Solange ich meine Sachen nur selbst benutze weiss ich das (ist ja schliesslich mein Design), aber wenn Arbeitskollegen meinen Code benutzen wissen sie das möglicherweise nicht. Bei einem shared_ptr erübrigt sich der Blick in die Doku.


  • Mod

    drakons code sieht besser so aus:

    while ( !cv_picture.empty() )
    {
      CPNG_FromFile* p = cv_picture.back();
      cv_picture.pop_back();
      delete p;
    }
    

    Warum:
    1. Ein Objekt zu zerstören, bevor alle Referenzen darauf entfernt wurden, ist immer schlechter Stil. Zerstörung sollte wo möglich stets den umgekehrten Vorgang zur Erzeugung darstellen.
    2. Handelt es sich um einen Container, der in mehreren Threads benutzt wird, ist der kritische Abschnitt unnötig lang (delete+erase statt nur back+pop).
    3. In Hinblick auf den inneren Aufbau von vector ist das Aufräumen von hinten vorzuziehen. Bei anderen Containern ist es zumindest nicht von Nachteil und in Hinblick zu Punkt 1. bildet es ggf. auch die Umkehrung der Erzeugung des Containers besser ab.



  • camper schrieb:

    drakons code sieht besser so aus:

    while ( !cv_picture.empty() )
    {
      CPNG_FromFile* p = cv_picture.back();
      cv_picture.pop_back();
      delete p;
    }
    

    Warum:
    1. Ein Objekt zu zerstören, bevor alle Referenzen darauf entfernt wurden, ist immer schlechter Stil. Zerstörung sollte wo möglich stets den umgekehrten Vorgang zur Erzeugung darstellen.
    2. Handelt es sich um einen Container, der in mehreren Threads benutzt wird, ist der kritische Abschnitt unnötig lang (delete+erase statt nur back+pop).
    3. In Hinblick auf den inneren Aufbau von vector ist das Aufräumen von hinten vorzuziehen. Bei anderen Containern ist es zumindest nicht von Nachteil und in Hinblick zu Punkt 1. bildet es ggf. auch die Umkehrung der Erzeugung des Containers besser ab.

    👍
    Habe mir das noch nie so überlegt. Danke für den Hinweis!
    Ist auch viel schöner zu lesen. 🙂



  • Okey, ich mache das ganze mit Pointers. Ich habe sowiso keine Lösung gefunden, dass ganze ohne Pointers zu machen. Und wenn es sowiso besser ist, kann mir dies ja eigentlich wurst sein.

    Dein Code um den Pointer-Vector zu löschen Camper, finde ich auch schöner, ich werde es auf jedenfall so machen.

    Bin dank eurer Hilfe wieder ein Stück weiter gekommen. Danke! 👍


Anmelden zum Antworten