Wo leake ich hier memory? Dynamic array



  • @marcel91200 sagte in Wo leake ich hier memory? Dynamic array:

    Anstatt einem Anfaenger einfach mal zu erklaeren was an einem Ausdruck falsch ist

    Der gesamte Ansatz ist Müll. Sorry. Das wird leider oft so gelehrt. Man kann es also nicht an einer Zeile oder so festmachen, denn der konzeptionelle Ansatz ist verkehrt. Ich habe dir den Link aus den Code-Guidelines gepostet. Wenn du new und/oder delete irgendwo im Programm hast und das nicht in einer speziell dafür gemachten Ressourcen-Handler-Klasse ist, dann taugt das Programm nichts. So einfach! Wenn dein Prof lehrt, dass ihr new und delete irgendwie einfach so im Programm benutzen sollt, dann kann er/sie nicht ordentlich in C++ programmieren. Häufig wird ein Mischmasch aus C und C++ gelehrt und im Endeffekt weder das eine noch das andere korrekt und idiomatisch.

    Bitte lies dir auch durch, was RAII bedeutet:
    https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization
    https://en.cppreference.com/w/cpp/language/raii
    https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-raii
    Das ist ein extrem wichtiges Konzept in C++. Wenn man es nicht befolgt (und dein Ansatz tut das nicht), dann kommen Programme heraus, die extrem fehleranfällig sind.

    Der Ton mag hier manchmal etwas hart sein, aber es nervt, wenn man gegen inkompetente Lehrkräfte ankämpfen muss, die ihren Studenten Dinge nicht ordentlich beibringen.

    Daher: wir haben hoffentlich dein Programm halbwegs so verbessert, dass du damit die Aufgabe erfüllst, aber so würde man das in ordentlichem C++ niemals machen. Nur wenn hier immer Bedingungen vorgegeben werden wie "keine STL", "keine eigene vector-Klasse", dann kann man das Problem eben nicht ordentlich lösen.

    Wunderschoener Umgangston hier teilweise...

    Bitte störe dich nicht daran, denn fachlich kompetent sind die Antworten, auch wenn der Ton manchmal etwas gröber ist. Auch wenn dir hier Fragen gestellt werden, ist das häufig mit der Intention, dass du darüber nachdenkst.



  • Ich hab gerade kurz RAII angeschaut und wollte nur mal fragen ob solch ein Ansatz demnach besser waere:

    struct dynamicArray
    {
        double *p;
        int size;
    
        void resize(int newSize)
        {
            p = (double *)realloc(p, newSize * sizeof(double));
            size = newSize;
        }
    
        dynamicArray(int newSize)
        {
            p = (double *)malloc(newSize * sizeof(double));
            size = newSize;
        }
        ~dynamicArray() { free(p);}
    };
    

    Waere das so richtig? Ich lagere dann das dynamische Array in ne eigene struct oder in ne eigene Klasse aus, da ich somit ja keine Bedenken mehr zwecks Speicherdeallozierung haette, da ja der Destructor dies, nach ende der Lebenszeit des Objekts, fuer mich uebernimmt.
    (Aequivalent haette man natuerlich auch new double[newSize] im Constructor nehmen koennen, falls das so richtig ist)

    Haette ein unique_ptr dem gegenueber irgendeinen Vorteil, ausser dass man sich den "Aufwand" ne eigene struct/klasse zu erstellen spart?



  • Naja, das wäre schon mal ein Ansatz.

    Gut: realloc (C) tut, sofern du nur POD-Werte hast, genau das richtige.

    Rat: Beginne Klassen mit Großbuchstaben. Also: DynamicArray.

    Du könntest genausogut new und delete verwenden. Sobald du nicht nur "double" erlauben willst, sondern beliebige Typen, ist malloc/free (das ist C!) keine Option mehr und du musst new/delete verwenden und realloc funktioniert dann nicht mehr. Solange du "einfache Typen" hast, ist realloc die optimale Lösung,

    Wichtig noch: was soll passieren, wenn du ein Objekt vom Typ DynamicArray kopieren willst oder mit = zuweisen willst? Am einfachsten ist es erstmal, das mit ... = delete zu verbieten.

    Haette ein unique_ptr dem gegenueber irgendeinen Vorteil, ausser dass man sich den "Aufwand" ne eigene struct/klasse zu erstellen spart?

    Nö, der hat normalerweise einen anderen Zweck.



  • Normalerweise beginne ich Klassen mit Grossbuchstaben, hab den Namen nur kopiert und deswegen nicht dran gedacht gerade ^^

    Wenn ich jetzt beliebige Typen verwende und realloc nicht mehr geht, dann koennte ich doch einfach wieder meinen Memcpy Ansatz verwenden oder?

    Wieso genau koennte ich keinen unique_ptr verwenden? Also ungefaehr so: std::unique_ptr<double[]> p(new double[N]); Dann muss ich den Speicher doch auch nicht selbst freigeben?

    Wegen der Zuweisung mit = :
    Dafuer kann man doch noch nen Copy Constructor schreiben, aber dachte der waere unnoetig, da ich ihn hier ja eh nicht brauchen werde.

    Noch eine kurze Frage:
    Bei dem Link: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-raii steht: "Do not pass a pointer or reference obtained from an aliased smart pointer" Was bedeutet denn "aliased smart pointer" genau, hab das im Internet nicht wirklich verstanden. Waere sowas std::unique_ptr<double[]> p(new double[N]) schon solch ein Pointer? Wenn ja, dann darf ich den nicht als Referenz oder Pointer in eine Funktion uebergeben, weil es zu Problemen fuehren kann, wenn dieser dort veraendert wird oder?



  • @marcel91200 sagte in Wo leake ich hier memory? Dynamic array:

    Waere sowas std::unique_ptr<double[]> p(new double[N]) schon solch ein Pointer?

    Nein. Da gehts nicht um spezifische Typen oder Arten wie etwas initialisiert wird sondern um Konzepte.

    @marcel91200 sagte in Wo leake ich hier memory? Dynamic array:

    Wenn ja, dann darf ich den nicht als Referenz oder Pointer in eine Funktion uebergeben, weil es zu Problemen fuehren kann, wenn dieser dort veraendert wird oder?

    Folglich auch quatsch.

    RAII nicht verstehen Du tust, auch niemals Smarpointer verstehen Du wirst, junger Padawan.

    Die Aufgabenstellung bekomm' ich noch immer nicht, richtig?



  • @marcel91200 sagte in Wo leake ich hier memory? Dynamic array:

    Haette ein unique_ptr dem gegenueber irgendeinen Vorteil, ausser dass man sich den "Aufwand" ne eigene struct/klasse zu erstellen spart?

    Nein, aber du könntest anstatt double *P; einen std::unique_ptr<double> p verwenden. Dann könntest du dir den Destruktor sparen und müsstest dir über die Copy Konstruktor und Assignment keine Gedanken machen, weil die per Default verboten wären.

    @marcel91200 sagte in Wo leake ich hier memory? Dynamic array:

    std::unique_ptr<double[]> p(new double[N]) schon solch ein Pointer?

    Nein, wie @Swordfish bereits gesagt hat, es geht nicht um den Typen. Es geht darum, dass du nicht auf Referenzen oder Pointer auf dem vom Smartpointer gehaltenen Objekt arbeiten sollst, falls der Smartpointer nicht lokal ist. Also, falls es zum Beispiel eine globale Variable ist, static ist oder ein Member ist.
    Der Grund ist, du kannst dann den Smartpointer ändern und der Pointer / die Referenz, die du übergeben hast ist ungültig: Sehr Fehleranfällig.



  • @Schlangenmensch sagte in [Wo leake ich hier memory? Dynamic array]

    Nein, aber du könntest anstatt double *P; einen std::unique_ptr<double> p verwenden. Dann könntest du dir den Destruktor sparen und müsstest dir über die Copy Konstruktor und Assignment keine Gedanken machen, weil die per Default verboten wären.

    std::unique_ptr<double[]> p wäre korrekt wenn es um ein Array geht.



  • @hustbaer Ähm, ja... sonst droht UB.



  • @Schlangenmensch Ähm... ja, und es geht hier um Arrays.

    ps: Ich bin nichtmal sicher ob UB droht. Könnte sein dass es bei Typen mit trivialem dtor sogar egal ist. Ist aber auf jeden Fall guter Stil es auch bei Typem mit trivialem dtor zu machen.



  • @hustbaer Man braucht es auf jeden Fall, wenn man den [] Operator haben möchte.



  • @marcel91200 sagte in Wo leake ich hier memory? Dynamic array:

    Wenn ich jetzt beliebige Typen verwende und realloc nicht mehr geht, dann koennte ich doch einfach wieder meinen Memcpy Ansatz verwenden oder?

    Nein. Es geht gerade darum, dass du nicht einfach beliebige Typen mit memcpy kopieren darfst (realloc ist im Prinzip dasselbe). Eben zum Beispiel Objekte, die im Konstruktor irgendwas tun. Oder Objekte, die einen Pointer auf eine Membervariable halten. Wenn du dort memcpy machst, ist das kopierte Objekt kaputt.

    Wieso genau koennte ich keinen unique_ptr verwenden? Also ungefaehr so: std::unique_ptr<double[]> p(new double[N]); Dann muss ich den Speicher doch auch nicht selbst freigeben?

    Richtig, aber das ist ja ein festes Array (und: bitte verwende std::make_unique). Was machst du, wenn du die Größe ändern willst? Wo ist deine resize-Funktion?

    Wegen der Zuweisung mit = :
    Dafuer kann man doch noch nen Copy Constructor schreiben, aber dachte der waere unnoetig, da ich ihn hier ja eh nicht brauchen werde.

    Ich sagte: du solltest den am einfachsten mit =delete löschen. Denn wenn du nichts schreibst, bekommst du einen automatischen Copy-Constructor, der eben elementweise kopiert - und das ist eben nicht richtig, wenn du eine struct mit {double *data; size_t size;} hast.



  • @Schlangenmensch sagte in Wo leake ich hier memory? Dynamic array:

    @hustbaer Man braucht es auf jeden Fall, wenn man den [] Operator haben möchte.

    Richtig.



  • Danke nochmal fuer die ganzen Antworten.

    Wegen der Aufgabenstellung:
    Die Aufgabe wurde mit uns nur in der Uebung durchgesprochen und dazu gab es dann eben noch die Dateien
    (+ne Header-Datei, wo nochmal die Permutationsfunktion und die isSorted beschrieben werden)
    Deswegen kann ich schlecht eine Aufgabenstellung schicken.

    Waere mein Programm dann so besser oder was sollte ich noch anders machen:

    #include "utils.hpp"
    
    #include <iostream>
    #include <sstream>
    #include <string>
    #include <memory>
    
    struct DynamicArray
    {
        double *p;
        int size;
    
        void resize(int newSize)
        {
            p = (double *)realloc(p, newSize * sizeof(double));
            size = newSize;
        }
    
        DynamicArray(int newSize)
        {
            p = (double *)malloc(newSize * sizeof(double));
            size = newSize;
        }
        ~DynamicArray() { free(p); }
        
    };
    
    DynamicArray userInputArray()
    {
        DynamicArray array(0);
        double tmp;
        int i = 0;
    
        while (true)
        {
            std::cout << "Enter " << i + 1 << " number:";
            std::cin >> tmp;
            if (std::cin.fail())
            {
                std::cin.clear();
                std::cin.ignore();
                break;
            }
            array.resize(++i);
            array.p[i-1] = tmp;
        }
        return array;
    }
    
    void getIndizes(int *indizes, int size)
    {
        for (int i = 0; i < size; i++)
        {
            std::cout << "Enter Indize: " << i + 1 << ":" << std::endl;
            std::cin >> indizes[i];
    
            while (std::cin.fail())
            {
                std::cout << "invalid input" << std::endl;
                std::cin.clear();
                std::cin.ignore();
                std::cout << "Enter Indize: " << i + 1 << " :" << std::endl;
                std::cin >> indizes[i];
            }
        }
    }
    
    int main(int, char **)
    {
        DynamicArray array = userInputArray();
        int size = array.size;
    
        auto indizes = std::make_unique<int[]>(size);
        getIndizes(indizes.get(), size);
        
        std::cout << isSorted(array.p, size, indizes.get());
    
        return 0;
    }
    

    (bei getIndizes hab ich nichts geaendert, die ist noch gleich)
    Sonst hab ich halt die struct DynamicArray hinzugefuegt und dann die paar Zeilen angepasst. Uebrigens duerfen wir die Parameter der Funktionen isSorted und isPermutation nicht aendern, falls dazu wieder was gesagt wird.

    Wegen dem CopyConstructor:
    Wenn ich den loesche, dann funktioniert ja beispielsweise diese Zeile nicht mehr: DynamicArray array = userInputArray();
    Wie loese ich das dann besser? Einfach das array in der main normal erstellen und als Referenz der Funktion userInputArray() uebergeben, damit dies dort dann angepasst wird?


  • Mod

    @marcel91200 sagte in Wo leake ich hier memory? Dynamic array:

    Wegen dem CopyConstructor:
    Wenn ich den loesche, dann funktioniert ja beispielsweise diese Zeile nicht mehr: DynamicArray array = userInputArray();
    Wie loese ich das dann besser? Einfach das array in der main normal erstellen und als referenz der Funktions userInputArray uebergeben, damit dies dort dann angepasst wird?

    Wieso solltest du den löschen wollen? Stand bei deiner Recherche zu RAII nirgendwo etwas zur "Rule of 3" (oder heutzutage eher "Rule of 5" bzw. "Rule of 0")? Das ist wichtig, das ein Destruktor mindestens immer von Kopierkonstruktor und Zuweisungsoperator begleitet wird!



  • @SeppJ sagte in Wo leake ich hier memory? Dynamic array:

    @marcel91200 sagte in Wo leake ich hier memory? Dynamic array:

    Wegen dem CopyConstructor:
    Wenn ich den loesche, dann funktioniert ja beispielsweise diese Zeile nicht mehr: DynamicArray array = userInputArray();
    Wie loese ich das dann besser? Einfach das array in der main normal erstellen und als referenz der Funktions userInputArray uebergeben, damit dies dort dann angepasst wird?

    Wieso solltest du den löschen wollen? Stand bei deiner Recherche zu RAII nirgendwo etwas zur "Rule of 3" (oder heutzutage eher "Rule of 5" bzw. "Rule of 0")? Das ist wichtig, das ein Destruktor mindestens immer von Kopierkonstruktor und Zuweisungsoperator begleitet wird!

    Naja siehe wob (oder ich hab die Aussage falsch verstanden)

    @wob sagte in Wo leake ich hier memory? Dynamic array:

    Ich sagte: du solltest den am einfachsten mit =delete löschen. Denn wenn du nichts schreibst, bekommst du einen automatischen Copy-Constructor, der eben elementweise kopiert - und das ist eben nicht richtig, wenn du eine struct mit {double *data; size_t size;} hast.



  • @marcel91200 Entweder Du verbietest das Benutzen oder Du implementierst ihn korrekt.

    https://mariusbancila.ro/blog/2018/07/26/cpp-special-member-function-rules/



  • @Swordfish sagte in Wo leake ich hier memory? Dynamic array:

    @marcel91200 Entweder Du verbietest das Benutzen oder Du implementierst ihn korrekt.

    https://mariusbancila.ro/blog/2018/07/26/cpp-special-member-function-rules/

    Wieso genau funktioniert der standard copy constructor nicht? In welchem Fall kommt es denn konkret zu Problemen? Bzw wie genau muss ich die Aussage von wob, die ich in der letzten Nachricht zitiert habe, verstehen?



  • struct foo {
        int *data;
        foo() : data{ new int } {}
        ~foo() { delete data; }
    };
    
    int main()
    {
        foo a;
        {
            foo b;
            a = b;  // a.data = b.data
        } // implicit: b.~foo();
    } // implicit: a.~foo();  --> boom.
    

    Google nach shallow vs deep copy.

    //edit: Diskussion über eine halbwegs no-bullshit Vectorimplementierung in dem Thread: https://www.c-plusplus.net/forum/topic/351034/run-time-check-failure-2



  • @marcel91200 sagte in Wo leake ich hier memory? Dynamic array:

    @Swordfish sagte in Wo leake ich hier memory? Dynamic array:

    @marcel91200 Entweder Du verbietest das Benutzen oder Du implementierst ihn korrekt.

    https://mariusbancila.ro/blog/2018/07/26/cpp-special-member-function-rules/

    Wieso genau funktioniert der standard copy constructor nicht? In welchem Fall kommt es denn konkret zu Problemen? Bzw wie genau muss ich die Aussage von wob, die ich in der letzten Nachricht zitiert habe, verstehen?

    Lies https://en.cppreference.com/w/cpp/language/rule_of_three
    Das Beispiel dort ist auch eines mit memcpy, new und delete - allerdings mit array of char statt double.

    Zitat: The implicitly-defined special member functions are typically incorrect if the class manages a resource whose handle is an object of non-class type (raw pointer, POSIX file descriptor, etc)



  • Achso, jetzt ergibts natuerlich Sinn wieso ich nen eigenen copy constructor machen soll.
    Sollte ich sonst noch irgendwas an dem Code aendern/verbessern?


Anmelden zum Antworten