Wieso gibt "das" keinen Memoryleak?



  • C++Fan 2010 schrieb:

    tntnet schrieb:

    Oder aber so:

    std::vector<char> buffer(8192);
    

    Dann brauche ich nur ein paar Bytes Stack. Zudem kann ich die Grösse auch dynamisch festlegen.

    Und vielfach langsamere Zugriffe als bei einem primitven Array. Aber gut, oft ist Tempo nicht das Entscheidende.

    Das stimmt nicht. Der Standard ist so formuliert, dass ein std::vector ohne overhead implementierbar ist. Der Zugriff auf einzelne Zeichen mit dem operator[] wird in der Regel vom Compiler optimiert, so dass Zugriff auf std::vector vom Maschinencode identisch mit dem Zugriff auf ein Array ist.

    Sicher ist das Anlegen des vectors langsamer, da hier ja eine Allokation stattfindet. Aber prinzipell ist es kein Unterschied, ob ich einen std::vector oder einen Zeiger mit malloc/free verwende.

    Und wer es nicht glaubt kann ja so etwas machen:

    std::vector<char> bufferv(8192);
    char* buffer = &buffer[0];
    

    Jetzt erklär mir mal, wie der buffer jetzt langsamer sein kann?

    Übrigens: hier ist die Implementierung des operator[] des gcc 4.4.1

    reference
          operator[](size_type __n)
          { return *(this->_M_impl._M_start + __n); }
    

    Wenn man genau hinschaut, sieht man, dass hier lediglich der Zeiger mit einem offset dereferenziert wird.



  • tntnet schrieb:

    Übrigens: hier ist die Implementierung des operator[] des gcc 4.4.1

    reference
          operator[](size_type __n)
          { return *(this->_M_impl._M_start + __n); }
    

    Wenn man genau hinschaut, sieht man, dass hier lediglich der Zeiger mit einem offset dereferenziert wird.

    Im Regelfall sind Zugriffe auf fixe Arrays mit ein, zwei Maschinenbefehlen erledigt. Mit einem std::vector brauchst Du mindestens noch eine Indirektion (Pointerzugriff). Zudem können Allokation und Erweiterung eines vectors scheitern. Wie Du siehst hat alles seine Vor- und Nachteile.



  • C++Fan 2010 schrieb:

    tntnet schrieb:

    Übrigens: hier ist die Implementierung des operator[] des gcc 4.4.1

    reference
          operator[](size_type __n)
          { return *(this->_M_impl._M_start + __n); }
    

    Wenn man genau hinschaut, sieht man, dass hier lediglich der Zeiger mit einem offset dereferenziert wird.

    Im Regelfall sind Zugriffe auf fixe Arrays mit ein, zwei Maschinenbefehlen erledigt. Mit einem std::vector brauchst Du mindestens noch eine Indirektion (Pointerzugriff).

    Wieder falsch.

    Ein Zugriff auf einen std::vector kann genauso effizient sein, wie der Zugriff auf jedes andere Array, solange es keine fixe Adresse hat. Dabei stört nichtmal die unnötige Indirektion über _M_impl sonderlich.
    Da nur globale/statische Variablen eine fixe Adresse haben, und man die normalerweise recht selten verwendet -> std::vector muss Zugriffe nicht bremsen.

    Maximal kann der Compiler sich "ein Register sparen", nämlich wenn er Stack-Arrays direkt über den Stack-Pointer/Frame-Pointer adressiert. Um ein dynamisches Array schnell zu adressieren, müsste er die Startadresse in einem Register halten, was natürlich den umgebenden Code etwas langsamer machen kann.



  • C++Fan 2010 schrieb:

    tntnet schrieb:

    Übrigens: hier ist die Implementierung des operator[] des gcc 4.4.1

    reference
          operator[](size_type __n)
          { return *(this->_M_impl._M_start + __n); }
    

    Wenn man genau hinschaut, sieht man, dass hier lediglich der Zeiger mit einem offset dereferenziert wird.

    Im Regelfall sind Zugriffe auf fixe Arrays mit ein, zwei Maschinenbefehlen erledigt. Mit einem std::vector brauchst Du mindestens noch eine Indirektion (Pointerzugriff). Zudem können Allokation und Erweiterung eines vectors scheitern. Wie Du siehst hat alles seine Vor- und Nachteile.

    Also eine Allokation und Erweiterung eines fixen Arrays kann nicht scheitern, weil es einfach nicht geht. Ist das dann ein Vorteil eines fixen Arrays? Ein Smart ist sicher auch sicherer als ein Ferrari, weil ich damit nicht mit 300 Sachen gegen die Wand fahren kann.

    Und ein fixes Array ist ja auch nur ein Zeiger, welcher genauso eine Indirektion erfordert. Es ist einfach egal, ob ich einen Zeiger verwende oder ob ich das in einer Klasse kapsele und dennoch den Zeiger implizit verwende. Aber so ähnlich hat das hustbaer ja auch schon gesagt.



  • Also eine Allokation und Erweiterung eines fixen Arrays kann nicht scheitern, weil es einfach nicht geht. Ist das dann ein Vorteil eines fixen Arrays?

    Je nach Anwendungsfall, ja.

    Ein Smart ist sicher auch sicherer als ein Ferrari, weil ich damit nicht mit 300 Sachen gegen die Wand fahren kann.

    Wenn man hauptsächlich im Innenbereich von Großstädten fährt, braucht man keine 300 km/h Endgeschwindigkeit, dafür die bessere Parkplatzwahrscheinlichkeit. Wenn ich also tatsächlich kein dynamisches Array brauche, dann verzichte ich gerne auf eine zusätzliche Fehlerquelle.



  • theliquidwave schrieb:

    Das ist dann wohl auch der Grund, warum der Stack innerhalb von großen Schleifen nicht so gerne benutzt wird

    Vor allem bei Rekursionen mit hoher Tiefe kommt man mit dem Stack mitunter schnell in Bedrängnis...



  • Tyrdal schrieb:

    Wenn ich also tatsächlich kein dynamisches Array brauche, dann verzichte ich gerne auf eine zusätzliche Fehlerquelle.

    Und wenn ich eins brauche nehm ich n std::vector<T> und hab das dynamische Array mit automatischer Speicherverwaltung und ohne besagte Fehlerquelle.



  • Nein, mit besagter Fehlerquelle. Größenänderungen haben bei std::vector mit dem Heap zu schaffen und können daher schiefgehen. Ich hab nix gegen vector, aber daß Allokation auf dem Heap vorkommen kann ein Nachteil sein.



  • Tyrdal schrieb:

    Nein, mit besagter Fehlerquelle. Größenänderungen haben bei std::vector mit dem Heap zu schaffen und können daher schiefgehen. Ich hab nix gegen vector, aber daß Allokation auf dem Heap vorkommen kann ein Nachteil sein.

    Was passiert eher: Dass eine Heapallokation schiefgeht und eine handlebare Exception fliegt oder dass einem das Programm um die Ohren fliegt weil man ein vergleichbar großes Array auf dem Stack anlegen wollte?



  • Was ist wenn man die Wahrscheinlichkeit des Schiefgehens auf 0% beschränken möchte und der Stack berechenbar ist?



  • Tyrdal schrieb:

    Wenn ich also tatsächlich kein dynamisches Array brauche, dann verzichte ich gerne auf eine zusätzliche Fehlerquelle.

    Wofür es auch std::tr1::array gibt, womit man die Fehlerquellen im Umgang mit C-Arrays reduziert...



  • Tyrdal schrieb:

    Was ist wenn man die Wahrscheinlichkeit des Schiefgehens auf 0% beschränken möchte und der Stack berechenbar ist?

    Das ist doch der seltene Ausnahmefall. Mann kann sich immer Fälle konstruieren, wo das schlechtere plötzlich das bessere ist. Um beim Auto zu bleiben: früher gab es auch Argumente gegen den Sicherheitsgurt. Da kann man im Notfall dran hängen bleiben und dadurch nicht schnell genug aussteigen.

    Wir diskutieren doch hier gerade den sparsamen Umgang mit dem Stack. Der Stack ist deutlich eingeschränkter als der Heap. Vor allem kann ich auf einen Stacküberlauf eigentlich nicht reagieren. Auf einen std::bad_alloc-Exception sehr wohl. Will ich zuverlässige Programme, sollte ich für irgendwie relevante Datenmengen den Heap bevorzugen.

    Wenn ich keinen dynamischen Puffer brauche, dann nehme ich trotzdem einen std::vector und verändere die Grösse halt nicht.



  • Deswegen schrieb ich ja auch "je nach Anwendungsfall". Richtig lesen hilft.

    Wenn ich keinen dynamischen Puffer brauche, dann nehme ich trotzdem einen std::vector und verändere die Grösse halt nicht.
    

    Kann man machen, finde da aber std::tr1::array passender.



  • Tyrdal schrieb:

    Wenn ich keinen dynamischen Puffer brauche, dann nehme ich trotzdem einen std::vector und verändere die Grösse halt nicht.
    

    Kann man machen, finde da aber std::tr1::array passender.

    Was dann auch wieder eine Entscheidungsfrage zwischen Heap und Stack ist. tr1::arrays liegen IIRC komplett auf dem Stack.



  • pumuckl schrieb:

    tr1::arrays liegen IIRC komplett auf dem Stack.

    Oder besser: Die Elemente sind Subobjekte von tr1::array. Natürlich kann ich auch schreiben

    std::tr1::array<int,10> *p = new std::tr1::array<int,10>;
    delete p;
    

    🙂



  • @pumuckl Klar das std::tr1::array macht nur ein Wrapper um nen array drum. Ich hab aber meist nur sehr kleine arrays, wenn die Größe vorher feststeht. Bei großen ist der Heap in den meisten Fällen besser. Soweit liegen unsere Meinungen nicht auseinander. 😉



  • Viel mehr als Speicher auf Heap oder Stack anfordern können die ganzen Bibliotheken etc. ja eh nicht, bringen also keine neuen Vor- oder Nachteile. 😉



  • Hi.
    Auch wenn ich dieses alte Thema nochmal aufgreife...
    Mir ist unklar, ob folgendes wirklich richtig ist:
    Wenn ich eine Klasse habe, welche eine Membervariable char szBla[32]; hat, und ihr im Code dann etwas zuweise:

    void CKlasse::setString()
    {
        strcpy(this->szBla, "roflsdbjndfjbhdnfl");
    }
    

    Wenn ich die Klasse lösche, ist dann der String auch weg?
    Ist das so richtig, also dass der String dauerhaft bestehen bleibt (also ab dem Aufruf von setString bis zum löschen der Klasse)?

    Gruß


  • Mod

    theliquidwave schrieb:

    Wenn ich die Klasse lösche, ist dann der String auch weg?

    Ich nehme mal an, mit Klasse meinst du eine Instanz der Klasse, auch Objekt genannt. szBla ist dann weg, wenn du das Objekt löscht (es sei denn, szBla ist static ). "roflsdbjndfjbhdnfl" bleibt.

    Ist das so richtig, also dass der String dauerhaft bestehen bleibt (also ab dem Aufruf von setString bis zum löschen der Klasse)?

    szBla existiert ab dem Konstruktoraufruf und bleibt bis zum destruktoraufruf. setString ändert nur den Inhalt. "roflsdbjndfjbhdnfl" ist ein Literal, das irgendwo im Programmcode steht und unabhängig von allen Einflüssen existiert.



  • theliquidwave schrieb:

    Hi.
    Auch wenn ich dieses alte Thema nochmal aufgreife...
    Mir ist unklar, ob folgendes wirklich richtig ist:
    Wenn ich eine Klasse habe, welche eine Membervariable char szBla[32]; hat, und ihr im Code dann etwas zuweise:

    void CKlasse::setString()
    {
        strcpy(this->szBla, "roflsdbjndfjbhdnfl");
    }
    

    Wenn ich die Klasse lösche, ist dann der String auch weg?
    Ist das so richtig, also dass der String dauerhaft bestehen bleibt (also ab dem Aufruf von setString bis zum löschen der Klasse)?

    Gruß

    Klassen werden nicht gelöscht, nur die Objekte. Das ist ein Unterschied.
    Und ja, das Array szBla des Objektes wird gelöscht, wenn das Objekt gelöscht wird. Wobei gelöscht nur heißt, dass der Speicher freigegeben wird, die Zeichen stehn dann vermutlich noch ne Weile an der Stelle im Speicher, bis der Speicher wieder benutzt wird.
    Der Hardcodierte C-String "roflsdbjndfjbhdnfl" steht im Code-Segment und zwar von programmstart bis Programmende.

    PS: strcpy ist PFUI 😛


Anmelden zum Antworten