array mit Zahlen füttern und sortieren



  • @Wade1234 sagte in array mit Zahlen füttern und sortieren:

    @It0101
    ja das meinte ich ja. raii bzw. das konzept dahinter (die bedeutung der bezeichnung musste ich erst einmal nachlesen) war mir ja an sich bekannt. aber wenn ich etwas in einen container stecke, muss ich das ja irgendwann auch mal wieder daraus entfernen und das kriegen die leute scheinbar nicht hin. im endeffekt ist das doch das gleiche, wie das vergessen von delete, oder nicht?

    Nein. Wenn du das Delete vergessen hast, hast du erfolgreich ein Memory-Leak produziert, während ein "übervoller" Container sich spätestens beim Beenden der Anwendung wieder aufräumt. D.h. es wurde technisch sauber umgesetzt, aber das Konzept passt in dem Fall nicht ( zu viele Kopieroperationen, ineffiziente Datenhaltung, etc. ), aber ein vergessenes Delete ist einfach schlampige Programmierung. An der falschen Stelle führt ein vergessenes Delete früher oder später zu std::bad_alloc.

    Der Idealfall ist, dass du über dein Konzept/Datenmodell ( Datentransport, Datenhaltung ) intensiv nachgedacht hast und dann durch Verwendung von STL-Containern auch noch sauber umgesetzt hast.



  • @Wade1234 Hast Du bei meinen beiden Beispielen eigentlich gesehen, warum "händisches" new und delete so wie in den Beispielen Käse ist und warum man wenn dann Smartpointer statt roher (besitzender) Zeiger nimmt? Irgendwie empfinde ich es schon ein wenig seltsam sich Mühe zu geben Dir etwas zu erklären und 0 Feedback zu bekommen.



  • @Wade1234
    Ich glaube du hast RAII noch nicht ganz verstanden. Ein kleines etwas anderes Beispiel:

    #include <iostream>
    #include <chrono>
    #include <thread>
    
    
    class MyLogger  // Rudimentäre auf Sichtbarkeit getrimmte Klasse zum Logging von Funktions- bzw. Blockaufrufen.
    {
    private:
        std::string mMessage;
        std::chrono::system_clock::time_point mStartPoint;  // Zeitpunkt an dem die Klasse angelegt wurde
    
    
    public:
        MyLogger(const std::string& Message)
        {
            mMessage = Message;
            mStartPoint = std::chrono::system_clock::now();
            std::clog << mMessage << " starts!\n";
        }
    
        MyLogger(const MyLogger&) = delete;               // Kopierkonstruktor wollen wir nicht
        MyLogger& operator=(const MyLogger&) = delete;    // Kopieroperator wollen wir auch nicht
    
        ~MyLogger()
        {
            std::chrono::system_clock::time_point EndPoint = std::chrono::system_clock::now();
            long long Diff = std::chrono::duration_cast<std::chrono::milliseconds>(EndPoint - mStartPoint).count();
    
            std::clog << mMessage << " ends after " << Diff << " ms!\n";
        }
    
    };
    
    
    
    int main()
    {
        MyLogger m("main()");
        {
            MyLogger m1("Block 1");
            {
                MyLogger m2("Block 2");
                {
                    MyLogger m3("Block 3");
                    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
                    // Sehr viel Code mit vielen return Anweisungen
    
    
                }
                std::this_thread::sleep_for(std::chrono::milliseconds(1000));
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
        return 0;
    }
    

    Die Klasse MyLogger loggt auf der Console wann eine (DLL) Funktion oder Block aufgerufen wurde und misst wie lange die Funktion oder Block benötigt.

    Nun stelle dir mal vor, du nutzt die Klasse in einer größeren Berechnungsfunktion mit hunderten von Codezeilen. Dummerweise sind in dieser Funktion sehr viele return Anweisungen. Dann müsstest du pro return Anweisung 3 Codezeilen einfügen, sicherstellen das du alle return Anweisungen gefunden hast und sicherstellen dass das auch in Zukunft passiert.

    Ziemlich viel fehleranfällige Arbeit.

    PS:
    Der Code funktioniert auch dann wenn der Berechnungscode Exceptions wirft.

    PPS:
    Habe Code entsprechend DocShoe's Wünschen angepasst.



  • Wenn du die Parameter des Kopierkonstruktors und Assignment-Operators noch const machst und clog statt printf benutzt fände ich das ein erstklassiges Beispiel.



  • @Swordfish
    naja die container machen das intern doch genauso, oder nicht? also abgesehen von "arbeitsersparnis", weil das schon jemand anderes gemacht hat, fällt mir da jetzt nichts auf.

    @Quiche-Lorraine sagte in array mit Zahlen füttern und sortieren:

    @Wade1234
    Ich glaube du hast RAII noch nicht ganz verstanden. Ein kleines etwas anderes Beispiel:

    naja sobald die jeweiligen anweisungsblöcke verlassen werden, verlieren m3, m2 und m1 ihre gültigkeit, wodurch der darin vorhandene string auch ungültig wird, was den aufruf des destruktors von string und dadurch die freigabe der ressourcen zur folge hat.



  • @Wade1234 sagte in array mit Zahlen füttern und sortieren:

    naja die container machen das intern doch genauso, oder nicht? also abgesehen von "arbeitsersparnis", weil das schon jemand anderes gemacht hat, fällt mir da jetzt nichts auf.

    Überleg mal was passiert wenn das zweite new[] wirft.


  • Mod

    @Wade1234 sagte in array mit Zahlen füttern und sortieren:

    @Swordfish
    naja die container machen das intern doch genauso, oder nicht? also abgesehen von "arbeitsersparnis", weil das schon jemand anderes gemacht hat, fällt mir da jetzt nichts auf.

    Sie machen's auch garantiert richtig. Ich würde viel Geld darauf verwetten, dass du es nicht schaffen würdest, eine 100% korrekte Umsetzung zu implementieren.



  • ja das ist mir auch klar. aber das grundsätzliche verständnis sollte man schon haben, oder?


  • Mod

    @Wade1234 sagte in array mit Zahlen füttern und sortieren:

    ja das ist mir auch klar. aber das grundsätzliche verständnis sollte man schon haben, oder?

    Wo ist denn hier das Verständnis? Bloßes Wissen um die Existenz bringt ja nichts, wenn die Schlüsselerkenntnis fehlt, wie man es korrekt benutzt. In dem Fall wäre es sogar besser, gar nichts von der Existenz gewusst zu haben, dann kann man es wenigstens nicht falsch machen.

    new gehört in eine ähnliche Schublade wie longjmp. Jede Erklärung, die nicht erklärt, wie man es richtig benutzt, und dass man es so gut wie nie braucht, ist unvollständig.



  • @Wade1234 sagte in array mit Zahlen füttern und sortieren:

    naja sobald die jeweiligen anweisungsblöcke verlassen werden, verlieren m3, m2 und m1 ihre gültigkeit, wodurch der darin vorhandene string auch ungültig wird, was den aufruf des destruktors von string und dadurch die freigabe der ressourcen zur folge hat.

    @Wade1234
    Jaein. Sobald beispielsweise m3 seine Gültigkeit verliert, wird der Destruktor aufgerufen. Dieser schreibt die Meldung "Block 3 ends after 1045 ms!". Nachdem der Destruktor fertig ist, verlieren m3.mMessage und m3.mStartPoint ihre Gültigkeit, s.d. auch deren Destruktoren aufgerufen werden. Der Destruktor von mMessage ruft bei mir übrigens _M_dispose() auf, was wohl den belegten Speicher freigibt.

    Ein Tipp: Nimm meinen Code, importiere diesen in einer IDE, setze Breakpoints bei den std::clog Anweisungen und debugge den Code. Das dürfte dir mehr helfen.



  • @Swordfish sagte in array mit Zahlen füttern und sortieren:

    Nein, Himmeldonnerwetter nochmal. RAII + RDID !!

    RRID





  • Ah, interessant. Ich kannte das bisher immer nur unter RRID. Finde ich auch passender als RDID, obwohl RDID anscheinend deutlich häufiger verwendet wird. Mir Wurst, ich bleibe bei RRID.

    EDIT: Nö, stimmt nicht. RRID wird doch mehr verwendet. Google
    "c++" "raii" "rrid"
    vs
    "c++" "raii" "rdid"


Anmelden zum Antworten