Korrektes Freigeben von Speicher

  • C++

Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von KidRick.

    Korrektes Freigeben von Speicher

    Hallo zusammen,

    wie gebe ich korrekt / ordnungsgemäß speicher wieder frei?

    Folgendes Beispiel. Ich habe eine Funktion die Bilder in Items lädt und diese anzeigt:

    C-Quellcode

    1. ​void loadItems() {
    2. foreach (QString photo, photos) {
    3. QListWidgetItem *item = new QListWidgetItem(photo);
    4. // usw
    5. listWidget->addItem(item);
    6. }
    7. }


    Funktioniert auch soweit ganz gut, durchs skalieren bzw. anzeigen der Thumbnails kommt auch bei großen Listen (5k Bilder) nicht wirklich eine starke Arbeitsspeicherauslastung zustande.

    Unabhängig davon, wenn ich nun auf einen Button klicke füge ich die Pfade zu einer Datei hinzu, würde aber natürlich den Speicher den ich vorher benutzt habe, wieder freigeben (die Items werden ja nun nichtmehr benötigt). Beispielsweise wenn ich den Abbrechen-Button drücke:

    C-Quellcode

    1. ​void onCancelButton_Clicked() {
    2. }


    Irgendeiner eine Idee/Ansatz? Ich hatte gedacht ich lade jedes Item zusätzlich in eine QList ​QList<QListWidgetItem*> items, lasse ebenfalls ein foreach drüber laufen und mache dann ein delete auf jedes einzelne Item. Wäre das sinnig?

    Wie gebe ich solchen Speicher allgemein korrekt frei? :)

    Ich danke schonmal!

    Frohes Fest!
    Jan
    Software being "Done" is like lawn being "Mowed". (Jim Benson)
    Allgemein gilt, dass Du Speicher auf dem Heap nur allozieren möchtest, wenn Du diesen außerhalb des aktuellen Methodenaufrufs brauchst (und er somit nicht nur im Stack verbleiben kann) und dieser danach wieder an einer anderen Stelle freigegeben wird. Dazu kann man das erstellte Objekt einmal zurückgeben und die Freigabepflicht an einen Aufrufer delegieren, der dies erledigt, sobald es nicht mehr benötigt wird. Andererseits kann man es auch irgendeinem anderen Objekt hinzufügen, dass dann u.U. auch das Löschen übernimmt. Wie es aussieht, nutzt Du Qt. Damit kenne ich mich nicht aus. Wenn Du der Liste allerdings Deine angelegten Items zuweist, muss in dieser sicher auch ein Weg existieren, wie diese freigegeben werden, ohne eine separate Liste mit Pointern zu führen. Sowas hab ich bisher auch nur eher selten gebraucht, weil man dann in der Regel eine solche Liste auch irgendwie zurückgeben oder woanders zuweisen würde. Sonst bräuchtest Du die Items ja auch nicht erstellen, wenn sie nirgends sonst eingesetzt werden.
    Habe gerade kurz gesucht und gesehen, dass im ListWidget auch Methoden existieren, die ein Item entfernen und Dir zurückgeben. An dieser Stelle könntest es dann z.B. löschen.

    KidRick schrieb:

    Wie gebe ich solchen Speicher allgemein korrekt frei?
    tl;dr: Sobald das Objekt nicht mehr gebraucht wird, eine Methode aufrufen, die es intern für Dich macht oder manuell das Objekt holen und löschen.

    Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:

    Trade schrieb:

    Allgemein gilt, dass Du Speicher auf dem Heap nur allozieren möchtest, wenn Du diesen außerhalb des aktuellen Methodenaufrufs brauchst (und er somit nicht nur im Stack verbleiben kann)

    Es gibt auch noch andere Gründe, aufgrund von Polymorphen Klassen zum Beispiel, da man Erbklassen nicht Basisklassen zuweisen kann wenn es nicht eine Referenz oder ein Pointer ist.
    Auch für dynamische Mengen, aber dafür gibt es std::vector. (Welches du Listen sowieso bevorzugen solltest, wenn dir Iteratorstabilität unwichtig ist)

    KidRick schrieb:


    Wie gebe ich solchen Speicher allgemein korrekt frei? :)

    Am besten einfach gar kein new und delete verwenden, niemals. (Es gibt sicher Ausnahmen, über die bin ich bis jetzt aber noch nicht gestolpert)
    Anstelle dessen nimmst du std::unique_ptr<QListWidgetItem> im container.
    Du kannst dann mit std::make_unique<QListWidgetItem>(Konstruktorparameter) einen erzeugen welcher intern new aufruft.
    Sobald die Liste dann zerstört wird, werden auch die Resourcen automatisch freigegeben. (Heißt, delete wird intern aufgerufen)
    Vorsicht aber, denn diese Biester sind einzelgänger, du kannst sie nicht kopieren.
    Anstelle dessen musst du wenn du unqiue_ptr weitergibst dies mit std::move tun.
    ----------------------------------------------------------------------------------------------------------------------

    Hier könnte meine Signatur stehen, aber die ist mir abfußen gekommen.

    ----------------------------------------------------------------------------------------------------------------------

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Elanda“ ()

    Ich kenne QList nicht aber wenn es etwas ähnliches wie Vector mit emplace_back hat, dann so:

    C-Quellcode

    1. items.emplace_back(std::make_unique<QListWidgetItem>(params...));

    Ersetze "emplace_back" mit deinem Äquivalent.

    Edit: Btw. bist du dir sicher das du eine List brauchst?
    Meistens braucht man nur vector und gut ists.

    Das ist einfach nur ein Array das mit den Elementen die man hinzufügt wächst.
    Der Vorteil ist das Vektoren in C++ garantieren eine zusammenhängende Kette an Elementen haben, während Listen ihre Elemente irgendwo im Speicher verstreuen.
    Ersteres hat den Vorteil das mehrere Elemente zugleich in den Prozessor-Cache geladen werden und daher den Vorzug besitzen nicht zurück in den RAM gehen zu müssen um neue Werte auszulesen, was eindeutig mehr Zeit beansprucht als die Werte im Cache auszulesen.
    (Siehe auch: de.m.wikipedia.org/wiki/Lokali…%A4umliche_Lokalit%C3%A4t)

    Der Nachteil ist das Iteratoren nicht mehr gültig sind sobald sich die Elemente im Vektor ändern (sofern nicht zuvor Speicher reserviert wurde), wohingegen die Liste nicht den gesamten Raum umsiedeln muss und dafür Iteratorstabilität garantiert.
    ----------------------------------------------------------------------------------------------------------------------

    Hier könnte meine Signatur stehen, aber die ist mir abfußen gekommen.

    ----------------------------------------------------------------------------------------------------------------------

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Elanda“ ()

    Elanda schrieb:

    Edit: Btw. bist du dir sicher das du eine List brauchst?
    Meistens braucht man nur vector und gut ists.
    QList<T> ist keine Linked-List, sondern vergleichbar mit std::vector<T*>, wobei die Elemente auf dem Heap landen (QList übernimmt das Memory-Management), oder wenn sizeof(T) <= sizeof(void*), vergleichbar mit std::vector<T>.

    KidRick schrieb:

    Unabhängig
    davon, wenn ich nun auf einen Button klicke füge ich die Pfade zu einer Datei hinzu, würde aber natürlich den Speicher den ich vorher benutzt habe, wieder freigeben
    Abbrechen klingt nach „Das Widget wird gleich eh zerstört.“ Wenn dabei dein QListWidget zerstört wird, werden automatisch alle Items auch zerstört: QListWidget::~QListWidget()
    Mit freundlichen Grüßen,
    Thunderbolt

    Elanda schrieb:

    aufgrund von Polymorphen Klassen zum Beispiel
    Stimmt. Bzgl. Zeigern sind mir in C++ bis auf Polymorphie und Iteratoren auch nicht so viele weitere Anwendungsfälle bekannt. Kommt halt auch immer drauf an, wie C-lastig der zugrundeliegende Code ist. Hab mich teilweise durch Projekte gewühlt, wo so gut wie nie irgendwelche Referenzen oder Smart Pointer genutzt wurden.

    Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    @KidRick In meinen alten C++-Quellen fand ich ein Makro, das den new-Befehl durch ein DEBUG_NEW ersetzte.
    Wenn iwelcher Speicher nicht korrekt freigegeben wurde, gab es eine entsprechende qualifizierte Debug-Ausgabe:

    C-Quellcode

    1. #ifdef _DEBUG
    2. #undef THIS_FILE
    3. static char THIS_FILE[]=__FILE__;
    4. #define new DEBUG_NEW
    5. #endif
    oben in die CPP-Datei einfügen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Thunderbolt schrieb:

    QList<T> ist keine Linked-List, sondern vergleichbar mit std::vector<T*>, wobei die Elemente auf dem Heap landen (QList übernimmt das Memory-Management), oder wenn sizeof(T) <= sizeof(void*), vergleichbar mit std::vector<T>.

    Ist ja mal wieder Spannend wie sehr sich die Masse uneinig über Benennungen sein kann.

    @KidRick @RodFromGermany
    Wie gesagt, verwende new/delete einfach gar nicht mehr. Smart pointer sind dafür da, dass du das nicht mehr tun musst.

    Das C++ Komitee hat so sehr darauf hingearbeitet nicht mehr in Abhängigkeit von expliziter Erzeugung/Zerstörung von Heap-Speicher zu sein, das sie smart pointer eingeführt hatten.
    Diese erleichtern nicht nur die Lesbarkeit, sondern händeln auch außergewöhnliche Fälle:
    Stell dir vor, am Anfang einer Funktion erzeugst du ein neues Objekt ohne smart pointer, welches du am Ende wieder zerstörst.
    Alles schön und gut oder?
    Absolut nicht, nein, nun stelle dir vor in der Mitte der Funktion wird eine Exception geworfen, der code wird delete niemals erreichen da der stack entladen wird und zum aufruf zurückkehrt ohne die Funktion zuende zu laufen.
    Nun hat man den Salat, und 'nen fetten Leak in der Bohne.
    Das wäre aber mit einem smart pointer nicht passiert, da er beim stack unwinding zerstört wird.

    Nun, auch bei smart pointern kannst du mit new ein Objekt erzeugen.
    Das Komitee hat aber std::make_unique erschaffen um dies zu verhindern, sie wollten den Entwicklern das new/delete aus den Köpfen schlagen.

    Wichtig zu Wissen, ja, smart pointer haben einen leichten Leistungseinfluss, allerdings ist dieser so uninteressant das dich das nicht zu kümmern braucht da du in Leistungskritischen Bereichen meistens sowieso keinen dynamischen Speicher verwenden solltest.

    Noch zum Abschluss gut zu Wissen ist eine Ausnahme wenn du tatsächlich new anstelle von make_unique zu verwenden hättest.
    Das wäre der Fall wenn dein smart pointer einen Benutzerdefinierten deleter hat, heißt, du erklärst unique_ptr wie/ob der Speicher aufgelöst wird.
    Darüber solltest du dir aber keine Sorgen machen, das wird erst wichtig wenn du mit unvollständigen Typen arbeitest.
    ----------------------------------------------------------------------------------------------------------------------

    Hier könnte meine Signatur stehen, aber die ist mir abfußen gekommen.

    ----------------------------------------------------------------------------------------------------------------------
    Interessant, wie eine kleine Diskussion hier entbrannt ist.

    Ich habs jetzt über ein Dialog gelöst... interessanterweise lag das Problem aber gar nicht an mir. Wenn ich 1xxx JPGs geladen habe, hat Qt die Bereinigung von selbst vorgenommen. Dasselbe hat das Plugin welches ich für Raw-Bilder nehme, nicht getan. Deshalb der Unterschied.

    Plugin angepasst ét voila, RAM-Auslastung und Speicherfreigabe funktioniert einwandfrei.
    Software being "Done" is like lawn being "Mowed". (Jim Benson)