Zeiger = 0;



  • 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?



  • Hmm so? Denke es wird noch fälscher sein, da das irgendwie den Kopf zum Rauchen bringt:

    void foo()
    {
        int* x = new int;
    
        try
        {
            if(hell_yeah())
                goto get_santa;
        }
        catch(...)
        {
            delete x;
            throw;
        }
    
        // ...
    
    a:
        bool bbla, bsolo;
        try
        {
            bbla = bla;
            bsolo = solo;
        }
        catch(...)
        {
            delete x;
            throw;
        }
    
        if (bbla)
        {
            delete x;
            return;
        }
    
        if (bsolo)
        {
            delete x;
            throw std::runtime_error("i have a bad feeling about this");
        }
    
        // ...
    
        delete x;
        return;
    
    get_santa:
        bool bhohoho;
        try
        {
            bhohoho = hohoho();
        }
        catch(...)
        {
            delete x;
            throw;
        }
    
        if(bhohoho)
        {
            delete x;
            x = new int(42);
            goto a;
        }
    
        delete x;
    }
    


  • Sieht schon besser aus, wobei ich natürlich nicht ausschließen möchte, dass ich etwas bzgl. Exceptions übersehen habe.

    Kommen wir also nun zum nächsten Teil. Du hast einige lokale Variablen eingeführt. Sie können einen Stack Overflow provozieren.



  • Nexus schrieb:

    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.

    1. Im Beispiel von Dot handelt es sich nicht um meinen Code den ich schreiben würde.
    2. Ich habe auch nicht vor im ganz normalen Code new und delete zu nutzen, nur in einigen Spezialfällen, einen davon habe ich oben als Beispiel angegeben, was ist denn so schwer daran zu verstehen?
    3. Das heißt nicht sofort dass jeder automatisch Fehler machen muss, ob ich das mache oder nicht ist mein Problem, meine Frage richtete sich nur auf Funktionalität und Theorie der Zeiger.

    Nexus schrieb:

    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.

    Ja die Hauptsache ist in der tat das korrekte Verhalten des Programms, jedoch kann man sich z.b. Zeit ersparen wenn man die Mittel der Standardbibliothek nutzt und nicht selbst sozusagen das Rad erfindet.

    Nexus schrieb:

    Und nein, du willst nicht Low-Level-Programmierung

    Und noch mal bin ich gezwungen diese Worte zu wiederholen: Ich muss selber wissen was ich vor habe.

    Nexus schrieb:

    zumindest nicht so wie du den Begriff verstehst.

    Warum bist du dir denn so sicher dass ich keinen blassen Schimmer davon habe, was dies bedeuten könnte? Vielleicht begründest du ja deine Meinung?

    Nexus schrieb:

    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.

    Siehe oben 2.

    Übrigens: wenn ich das bewusst mache, muss es auch einen Grund dafür geben, ich mache mir nicht umsonst die Arbeit schwerer, oder für welchen Idioten hällst du mich, sag mir das mal.



  • Gegen unique_ptr gibt es keinen Grund. Er ist genau gleich schnell wie ein roher Zeiger, nur besser.



  • Kellerautomat schrieb:

    Gegen unique_ptr gibt es keinen Grund. Er ist genau gleich schnell wie ein roher Zeiger, nur besser.

    Wie oft soll ich denn noch wiederholen dass es nicht meine Aufgabenstellung ist, meine ist es den Code komplett selber zu schreiben!

    UND NEIN, es handelt sich gerade nicht um die Entwicklung eines Programms, sondern um DIE THEORETISCHEN ZEIGER KENNTNISSE.



  • "Komplett selber schreiben" ist bestimmt nicht deine Anforderung. Oder schreibst du dir die STL+Iostreams auch selbst? 🤡
    Du kannst dir übrigens auch selbst einen unique_ptr implementieren.



  • Ich habe ein paar Seiten davor eine Beispielsimplementierung eines shared_ptr gepostet, sowas bekommt man sehr schnell hin und es ist "selbst gemacht" ...



  • Ethon schrieb:

    Ich habe ein paar Seiten davor eine Beispielsimplementierung eines shared_ptr gepostet, sowas bekommt man sehr schnell hin und es ist "selbst gemacht" ...

    Und ich habe Beispielcode gepostet, nur wurde der leider ignoriert!

    Bin Schritt für Schritt durchs Programm gegangen um sicherzustellen, dass es wirklich ohne Speicherleck funktionieren müsste, nur wurde auch dieser Beitrag einfach ignoriert!


Anmelden zum Antworten