Zeiger = 0;



  • dot schrieb:

    Man sollte vielleicht darauf hinweisen, dass dort wo //... natürlich nicht nur x gültig sein muss, sondern dass dort potentiell auch Exceptions fliegen können...

    Jajaja. Das ist mir auch gerade in den Sinn gekommen. Nicht nur bei Funktionen wie hohoho() oder hell_yeah() ...

    Dann fängt man die Exceptions aber einfach, oder? Und gibt den Speicher von x frei?



  • Bezüglich der Antwort:

    Aber das ist doch keine Schuld des Operators?! Es ist das Problem des Programmierers ob er Fehler macht oder nicht, das hättet ihr zu aller Anfang auch sagen müssen, ich weiß was ich tue, hatte nur ein wenig theoretische Hilfe bezüglich der Funktionalität eines Zeigers und deren Benutzung gebraucht...

    1. Wenn man den Code richtig schreibt wird das Programm auch ohne Fehler funktionieren.
    2. Mein Ziel ist es nicht möglichst viel Zeit zu sparen nach dem Motto: "Hauptsache es läuft richtig", mein Ziel ist schon von Anfang an die Low-Level Programmierung gewesen.
    3. Befolge ich lieber die Regel, goto erst gar nicht zu nutzen!

    class Data;
    
    class Pointer
    { 
    private:
    
        Data* data;
    
    public:
    
        Pointer(Data& d)
        {
            data = new Data(d);
        }
    
        ~Pointer()
        {
            delete data;
            data = 0;
        }
    }; 
    
    int main(void)
    {
        Data d;
        Pointer* p = new Pointer(d);
    
        delete p;
        p = 0;
    
        return 0;
    }
    

    Aaaaaalso, noch mal Schritt für Schritt:

    1 >> Eine Instanz namens 'd' der Klasse Data wird angelegt.

    2 >> Ein Zeiger namens 'p' der Klasse Pointer wird angelegt. Status: 'p' zeigt ins Nichts.
    2.1 >> Operator new ruft den Konstruktor der Klasse Pointer auf und erzeugt 1 neues Objekt der Klasse Pointer in 'p'. Status: 'p' zeigt auf Speicheradresse des neuen Objekts.
    2.2 >> 'd' Wird als Konstruktorparameter übergeben.
    2.3 >> auf den Zeiger Pointer::data wird per Operator new 1 Objekt der Klasse Data angelegt welches per Kopierkonstruktor mit 'd' initialisiert wird. Status: 'data' zeigt auf Speicheradresse des neuen Objekts.

    3 >> Operator delete ruft den Destruktor des Objekts in 'p' auf
    3.1 >> über delete wird der Destruktor des Objekts, welcher sich in Pointer::data befindet, aufgerufen, das Objekt in 'data' wird gelöscht. Status: 'data' zeigt auf Speicheradresse des gelöschten Objekts.
    3.2 >> Pointer::data wird auf 0 Gesetzt (was in diesem Beispiel aber nicht wirklich nötig ist) da sich auf dessen Adresse Nichts mehr befindet. Status: 'data' zeigt ins Nichts.

    4 >> Zeiger 'p' wird auf 0 gesetzt da sich auf dessen Adresse Nichts mehr befindet Status: 'p' zeigt ins Nichts.

    5 >> Maximale Reichweite von 'p' wird erreicht
    4.1 >> Destruktor von 'p' wird aufgerufen, das Objekt wird gelöscht.

    6 >> Das Programm wird beendet, 0 wird an das Betriebssystem zurückgeben.

    P.S. Gratuliere alle zum Sieg :schland:



  • 1. Wenn man den Code richtig schreibt wird das Programm auch ohne Fehler funktionieren.
    2. Mein Ziel ist es nicht möglichst viel Zeit zu sparen nach dem Motto: "Hauptsache es läuft richtig", mein Ziel ist schon von Anfang an die Low-Level Programmierung gewesen.
    3. Befolge ich lieber die Regel, goto erst gar nicht zu nutzen!

    1. Nullaussage
    2. Low-Level? Glaub mir, die willst du nicht machen.
    3. Jau.

    RussianTux schrieb:

    Aaaaaalso, noch mal Schritt für Schritt:

    1 >> Eine Instanz namens 'd' der Klasse Data wird angelegt.

    2 >> Ein Zeiger namens 'p' der Klasse Pointer wird angelegt. Status: 'p' zeigt ins Nichts.
    2.1 >> Operator new ruft den Konstruktor der Klasse Pointer auf und erzeugt 1 neues Objekt der Klasse Pointer in 'p'. Status: 'p' zeigt auf Speicheradresse des neuen Objekts.
    2.2 >> 'd' Wird als Konstruktorparameter übergeben.
    2.3 >> auf den Zeiger Pointer::data wird per Operator new 1 Objekt der Klasse Data angelegt welches per Kopierkonstruktor mit 'd' initialisiert wird. Status: 'data' zeigt auf Speicheradresse des neuen Objekts.

    3 >> Operator delete ruft den Destruktor des Objekts in 'p' auf
    3.1 >> über delete wird der Destruktor des Objekts, welcher sich in Pointer::data befindet, aufgerufen, das Objekt in 'data' wird gelöscht. Status: 'data' zeigt auf Speicheradresse des gelöschten Objekts.
    3.2 >> Pointer::data wird auf 0 Gesetzt (was in diesem Beispiel aber nicht wirklich nötig ist) da sich auf dessen Adresse Nichts mehr befindet. Status: 'data' zeigt ins Nichts.

    4 >> Zeiger 'p' wird auf 0 gesetzt da sich auf dessen Adresse Nichts mehr befindet Status: 'p' zeigt ins Nichts.

    5 >> Maximale Reichweite von 'p' wird erreicht
    4.1 >> Destruktor von 'p' wird aufgerufen, das Objekt wird gelöscht.

    6 >> Das Programm wird beendet, 0 wird an das Betriebssystem zurückgeben.

    P.S. Gratuliere alle zum Sieg :schland:

    So ganz grob alles richtig. Reihenfolge manchmal falsch, 5. verstehe ich nicht, meinst du etwa das der Scope verlassen wird?- sonst...

    Zeiger zeigen übrigens nicht einfach ins Nichts.



  • Hacker schrieb:

    1. Wenn man den Code richtig schreibt wird das Programm auch ohne Fehler funktionieren.
    2. Mein Ziel ist es nicht möglichst viel Zeit zu sparen nach dem Motto: "Hauptsache es läuft richtig", mein Ziel ist schon von Anfang an die Low-Level Programmierung gewesen.
    3. Befolge ich lieber die Regel, goto erst gar nicht zu nutzen!

    1. Nullaussage
    2. Low-Level? Glaub mir, die willst du nicht machen.
    3. Jau.

    1. Also funktioniert das Programm ohne Fehler selbst wenn der Programmierer falsch programmiert? x))
    2. Vielleicht will ich damit ja anfangen? Weiß doch keiner so genau was ich möchte 🙂
    3 Jo.

    [quote="Hacker"]

    RussianTux schrieb:

    Aaaaaalso, noch mal Schritt für Schritt:

    1 >> Eine Instanz namens 'd' der Klasse Data wird angelegt.

    2 >> Ein Zeiger namens 'p' der Klasse Pointer wird angelegt. Status: 'p' zeigt ins Nichts.
    2.1 >> Operator new ruft den Konstruktor der Klasse Pointer auf und erzeugt 1 neues Objekt der Klasse Pointer in 'p'. Status: 'p' zeigt auf Speicheradresse des neuen Objekts.
    2.2 >> 'd' Wird als Konstruktorparameter übergeben.
    2.3 >> auf den Zeiger Pointer::data wird per Operator new 1 Objekt der Klasse Data angelegt welches per Kopierkonstruktor mit 'd' initialisiert wird. Status: 'data' zeigt auf Speicheradresse des neuen Objekts.

    3 >> Operator delete ruft den Destruktor des Objekts in 'p' auf
    3.1 >> über delete wird der Destruktor des Objekts, welcher sich in Pointer::data befindet, aufgerufen, das Objekt in 'data' wird gelöscht. Status: 'data' zeigt auf Speicheradresse des gelöschten Objekts.
    3.2 >> Pointer::data wird auf 0 Gesetzt (was in diesem Beispiel aber nicht wirklich nötig ist) da sich auf dessen Adresse Nichts mehr befindet. Status: 'data' zeigt ins Nichts.

    4 >> Zeiger 'p' wird auf 0 gesetzt da sich auf dessen Adresse Nichts mehr befindet Status: 'p' zeigt ins Nichts.

    5 >> Maximale Reichweite von 'p' wird erreicht
    4.1 >> Destruktor von 'p' wird aufgerufen, das Objekt wird gelöscht.

    6 >> Das Programm wird beendet, 0 wird an das Betriebssystem zurückgeben.

    P.S. Gratuliere alle zum Sieg :schland:

    So ganz grob alles richtig. Reihenfolge manchmal falsch, 5. verstehe ich nicht, meinst du etwa das der Scope verlassen wird?- sonst...

    Genau das habe ich damit gemeint, die "Reichweite" eines Objekts ist ja auch eigentlich die Länge des Anweisungsblocks.

    Hacker schrieb:

    Zeiger zeigen übrigens nicht einfach ins Nichts.

    Jo das ist klar, soll einfach die 0 symbolisieren, denn theoretisch zeigt der Zeiger ja auch "ins Leere" wenn er == 0 ist, oder versteh ich das irgendwie falsch?



  • Hacker schrieb:

    @Kellerautomat: Wieso Pufferst du da? Du brauchst doch tmp gar nicht.

    Weißt du überhaupt, was puffern ist? 🙄
    Ohne scheiß, hör mal auf zu posten. Deine restlichen Posts sind auch voller Humbug.

    Und um deine falsch gestellt Frage zu beantworten: Exception-Safety. Stichwort: Starke Garantie.

    Michael E. schrieb:

    Kellerautomat: Dein Code tut nicht mal mehr dasselbe.

    Hab ich irgendwo was falsch kopiert? Hab darauf jetzt nicht geachtet. 🤡

    Michael E. schrieb:

    Ganz davon abgesehen, dass dein Code nicht Exception-safe ist.

    Wo denn?



  • RussianTux schrieb:

    ich weiß was ich tue

    nicht mal annähernd

    2. Mein Ziel ist es nicht möglichst viel Zeit zu sparen nach dem Motto: "Hauptsache es läuft richtig", mein Ziel ist schon von Anfang an die Low-Level Programmierung gewesen.

    siehe oben. da du keine ahnung hast wovon du redest, ...

    aber ist gut dass du dich so gut mit Hacker verstehst. der hat auch keinen tau von irgendwas, kann dir also im keinen-tau-haben gut weiterhelfen.



  • hustbaer schrieb:

    RussianTux schrieb:

    ich weiß was ich tue

    nicht mal annähernd

    2. Mein Ziel ist es nicht möglichst viel Zeit zu sparen nach dem Motto: "Hauptsache es läuft richtig", mein Ziel ist schon von Anfang an die Low-Level Programmierung gewesen.

    siehe oben. da du keine ahnung hast wovon du redest, ...

    aber ist gut dass du dich so gut mit Hacker verstehst. der hat auch keinen tau von irgendwas, kann dir also im keinen-tau-haben gut weiterhelfen.

    Ich bin oben das Beispiel Schritt für Schritt durchgegangen und der Theorie zur Folge müsste es doch keinen Speicherleck geben? Wäre besser wenn du mir hierbei helfen würdest 🙂 anstatt andere anzuprangern die vielleicht so wenig Ahnung haben wie ich



  • Kellerautomat schrieb:

    Hab ich irgendwo was falsch kopiert? Hab darauf jetzt nicht geachtet. 🤡

    Betrachte mal den Ablauf, wenn hell_yeah() zu false evaluiert wird. Oder wenn bla == true ist. Oder...

    Michael E. schrieb:

    Ganz davon abgesehen, dass dein Code nicht Exception-safe ist.

    Wo denn?

    Z.B. double delete bei hell_yeah() == true, hohoho() == true, bla == false, solo == true . Oder der einfachste Fall, wenn hell_yeah() == false .



  • Michael E. schrieb:

    Z.B. double delete bei hell_yeah() == true, hohoho() == true, bla == false, solo == true . Oder der einfachste Fall, wenn hell_yeah() == false .

    Also gut, es fehlt ein delete nach dem try-catch. Aber sonst seh ichs grad nicht.



  • Kellerautomat schrieb:

    Also gut, es fehlt ein delete nach dem try-catch.

    Nicht nach dem catch, sondern am Ende des try-Blocks.

    Aber sonst seh ichs grad nicht.

    Mit den eben genannten Parametern hast du folgenden Ablauf:

    void a(int* x)
    {
      if (bla)                                                          // 9
        return;
    
      if (solo)                                                         // 10
        throw std::runtime_error("i have a bad feeling about this");    // 11
    }
    
    void get_santa(int* x)
    {
      if (hohoho())                                                     // 4
      {
        int* tmp = new int(42);                                         // 5
        delete x;                                                       // 6 -> erstes delete
        x = tmp;                                                        // 7
        a(x);                                                           // 8
      }
    }
    
    void foo()
    {
      int* x = new int;                                                 // 1
    
      try
      {
        if (hell_yeah())                                                // 2
          get_santa(x);                                                 // 3
      }
    
      catch(...)                                                        // 12
      {
        delete x;                                                       // 13 -> zweites delete auf demselben Objekt
        throw;
      }
    }
    

    Weitere Fehler nenne ich, wenn du zum Originalcode äquivaltenten Code schreibst.



  • Michael E. schrieb:

    Nicht nach dem catch, sondern am Ende des try-Blocks.

    Das ist doch genau dasselbe.

    Michael E. schrieb:

    Mit den eben genannten Parametern hast du folgenden Ablauf:

    void a(int* x)
    {
      if (bla)                                                          // 9
        return;
    
      if (solo)                                                         // 10
        throw std::runtime_error("i have a bad feeling about this");    // 11
    }
    
    void get_santa(int* x)
    {
      if (hohoho())                                                     // 4
      {
        int* tmp = new int(42);                                         // 5
        delete x;                                                       // 6 -> erstes delete
        x = tmp;                                                        // 7
        a(x);                                                           // 8
      }
    }
    
    void foo()
    {
      int* x = new int;                                                 // 1
    
      try
      {
        if (hell_yeah())                                                // 2
          get_santa(x);                                                 // 3
      }
    
      catch(...)                                                        // 12
      {
        delete x;                                                       // 13 -> zweites delete auf demselben Objekt
        throw;
      }
    }
    

    Und bei 7 wird ein neuer Wert zugewiesen, also nirgens double delete.



  • RussianTux schrieb:

    Aber das ist doch keine Schuld des Operators?! Es ist das Problem des Programmierers ob er Fehler macht oder nicht

    Nein.

    Manuelle Speicherverwaltung ist ein Sprachmittel, das -- wenn es im normalen Anwendungscode verwendet wird -- eine enorme Fehleranfälligkeit provoziert. In vielen Situationen haben machen selbst Programmierer, die new und delete kennen müssten, Fehler, siehe Beispiel von dot.

    RussianTux schrieb:

    2. Mein Ziel ist es nicht möglichst viel Zeit zu sparen nach dem Motto: "Hauptsache es läuft richtig", mein Ziel ist schon von Anfang an die Low-Level Programmierung gewesen.

    Natürlich besteht die Hauptsache darin, dass ein Programm richtig läuft. Ich weiss gar nicht, wie man das anders sehen kann. Ein Programm, das nicht macht, was es soll, ist nutzlos.

    Und nein, du willst nicht Low-Level-Programmierung, zumindest nicht so wie du den Begriff verstehst. Es spricht rein gar nichts dafür, im normalen Code dauernd new und delete zu verwenden und bewusst auf die besseren Alternativen wie std::unique_ptr zu verzichten. Warum sollte man das tun, wenn man die Problematik kennt? Sowas ist dumm und unproduktiv.



  • Hallo Kellerautomat,

    Kellerautomat schrieb:

    Und bei 7 wird ein neuer Wert zugewiesen, also nirgens double delete.

    Aber du weist in get_santa() nur der lokalen Variablen x einen neuen Wert zu (und das x aus foo() hat immer noch seinen ursprünglichen Wert) 😉



  • Man braucht garnicht zu diskutieren, durch die Anwesenheit von Exceptions ist RAII Pflicht. Du kannst praktisch nie sicher wissen, ob eine Funktion wirft oder nicht.

    Wieso zur Hölle legst du überhaupt etwas mit new an? Muss ich fast nie.



  • Th69 schrieb:

    Hallo Kellerautomat,

    Kellerautomat schrieb:

    Und bei 7 wird ein neuer Wert zugewiesen, also nirgens double delete.

    Aber du weist in get_santa() nur der lokalen Variablen x einen neuen Wert zu (und das x aus foo() hat immer noch seinen ursprünglichen Wert) 😉

    Ah, jetzt kommen wir der Sache schon näher. 🤡
    Daran hab ich jetzt wirklich nicht gedacht, sollte natürlich ein int*& sein...



  • Kellerautomat schrieb:

    Michael E. schrieb:

    Nicht nach dem catch, sondern am Ende des try-Blocks.

    Das ist doch genau dasselbe.

    In diesem Fall hast du Recht, weil du weiterwirfst. Im Allgemeinen macht es natürlich einen Unterschied.

    Versucht sich noch jemand an der Aufgabe von dot? Bis jetzt wurde noch keine zufriedenstellende Lösung gepostet.



  • So vielleicht?

    void foo()
    {
        int* x = new int;
    
        decltype(hell_yeah()) result;
        try
        {
            result = hell_yeah();
        }
        catch(...)
        {
            delete x;
            throw;
        }
    
        if(result)
            goto get_santa;
    
        // ...
    
    a:
    
        if (bla)
        {
            delete x;
            return;
        }
    
        if (solo)
        {
            delete x;
            throw std::runtime_error("i have a bad feeling about this");
        }
    
        // ...
    
        delete x;
        return;
    
    get_santa:
        try
        {
            decltype(hohoho()) result;
            try
            {
                result = hohoho();
            }
            catch(...)
            {
                delete x;
                throw;
            }
    
            if(result)
            {
                delete x;
                x = new int(42);
                goto a;
            }
        }
        catch(std::bad_alloc const& e)
        {
            throw;
        }
    
        delete x;
    }
    


  • Ethon schrieb:

    So vielleicht?

    bla kann bei der Konvertierung zu bool werfen. decltype(hell_yeah()) muss nicht default-constructible sein.



  • bla ist doch nirgendswo definiert, deswegen bin ich von einem Integer ausgegangen. 😉
    Die if-Abfrage müsste man einfach in das try hineinziehen, dann hat man doch Problem mit nicht-defaultkonstruierbaren Typen nicht.



  • Ethon schrieb:

    bla ist doch nirgendswo definiert, deswegen bin ich von einem Integer ausgegangen. 😉

    Die Aufgabenstellung war "immer und unter allen Umständen". Deshalb darfst du nicht vom einfachsten Fall ausgehen :p

    Die if-Abfrage müsste man einfach in das try hineinziehen, dann hat man doch Problem mit nicht-defaultkonstruierbaren Typen nicht.

    Willst du dann auf den ganzen decltype-Kram verzichten oder wie ist das gemeint?


Anmelden zum Antworten