delete mit 2 pointern



  • Hallo Leute,

    ich bin mit der Antwort die ich gefunden habe nicht ganz glück
    http://stackoverflow.com/questions/772146/c-delete-object-referenced-by-two-pointers

    Habe folgendes Problem:

    int *i = new int; 
    int *p = i; 
    
    delete i; 
    
    if(p!= NULL){ 
      delete p; 
    }
    

    Also ich habe 2 Pointer (i und p) die auf den gleichen Speicherbereich zeigen. Wird mit delete der Speicherbereich durch einen Pointer i frei geräumt, stellt sich die Frage: Worauf zeigt der andere Pointer p dann?

    Die im Link (stackoverflow.com) angegebene Lösung

    int *i = new int; 
    int *p = i; 
    
    if(p != i){ 
    delete p; 
    } 
    delete i;
    

    ist insofern nicht befriedigend, weil ich zum Zeitpunkt delete p nicht weiß ob delete i schon aufgeführt wurde...

    Es hat den Anschein, dass dies ein Konstruktionsproblem ist,
    oder hat jmd eine Idee wie ich nur von p erfahre ob der Speicher auf den p zeigt schon freigegeben wurde?



  • Ich würde da jetzt gar nicht gross ein Workaround bauen, sondern gleich einen intelligenten Pointer her nehmen, welcher so etwas für dich machen kann.

    Mit boost::shared_ptr und boost::weak_ptr kann man so etwas ganz gut verwalten:
    http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/smart_ptr.htm



  • 😉
    Ich hätt gern von Anfang an auf boost::shared_ptr gesetzt.
    Der Zug ist jetzt aber leider abgefahren (nachträgliche änderung zu groß). Das Projekt muss als erste Version jetzt vom Tisch.
    Derzeit behelfe ich mir die Datenstruktur einfach zu kopieren:
    also

    int *i = new int;
    int *p = new int(i);
    
    delete i;
    
    if(p!= NULL){
      delete p;
    }
    

    Da die Datenstruktur kein int sondern ein std::vector mit ca 20 Elementen à 4 int Werten ist, mag die Kopie gehen - ist aber durchaus nicht schön.

    Kann man keine exception durch das fehlerhafte ausführen von delete p im urpsprünglichen Bsp fangen!?



  • Sehe ich das richtig, dass ja delete p nicht aufgerufen wird, da ja die Bedingung p != i nicht zutrifft.
    So dann wird ja nur delete i ausgeführt.
    Dadurch zeigt doch p immer noch auf den Speicher wo vorher der Wert von *i war oder nicht?

    Gruß freeG

    EDIT:

    Bezieht sich auf den 1.Post 2. Codebeispiel.



  • Ka55i0peia schrieb:

    Derzeit behelfe ich mir die Datenstruktur einfach zu kopieren:

    Huch, wenn Kopien kein Problem sind, warum verwendest du dann überhaupt Zeiger?
    Und dein Codebeispiel ist hoffentlich nicht Original, denn Zeile 2 zieht definitiv keine Kopie...



  • Ka55i0peia schrieb:

    ist insofern nicht befriedigend, weil ich zum Zeitpunkt delete p nicht weiß ob delete i schon aufgeführt wurde...

    Das macht keinen Sinn.

    Du redest die ganze Zeit von dem Problem dass in und out auf das selbe Zeigen koennen. Wenn du aber nichtmal garantieren kannst dass du ueberhaupt eins der beiden deleten darfst, hast du ja ganz andere Probleme.

    Du musst definieren wer der Besizter ist. Du kannst zB auch Problemlos dem Caller die Loeschverantwortung geben - was meist sowieso sinnvoller ist wenn du nur Zeiger nimmst.

    Also deshalb nochmal:

    Warum weisst du nicht ob du etwas loeschen darfst. Sobald du das definiert hast - hast du alle Probleme geloest. In C++ muss immer klar definiert werden wer das delete zu einem new macht.

    Dann hast du auch mit 50 Zeigern kein Problem.

    PS:
    Deinem 2. Post entnehme ich, dass du sehr wohl definiert hast wer loescht?
    Dann ist

    if(i!=p) delete i;
    delete p;
    

    genau das was du suchst.



  • korrekt.
    Das war ja auch der vorschlag, der in stackoverflow.com beschrieben wurde.

    Dadurch zeigt doch p immer noch auf den Speicher wo vorher der Wert von *i war

    -> Also zeigt p dann auf irgendwas...!?

    Seh ich p irgendwie an, dass sein Speicherbereich ungültig ist?

    Edit:
    @Shade of Mine:

    In C++ muss immer klar definiert werden wer das delete zu einem new macht.

    Das ist mir schon klar. Die Frage war, ob man einem Pointer "ansieht", dass sein Bereich auf den er zeigt bereits ungültig ist. So wie ich das sehe, geht das nicht so ohne weiteres.

    Ergo, ist die modellierung der Speicherverwaltung in diesem Programmteil mist. Trotzdem vielen Dank für die Hinweise.

    Falls jmd doch eine Idee hat, ob ich die Gültigkeit eines Pointerverweises testen kann, wäre das noch ein sinnvoller Beitrag!



  • Ich habe es mir zur Angewohnheit gemacht, den Pointer nach delete auf 0 (NULL) zu setzen.

    Da die Datenstruktur kein int sondern ein std::vector mit ca 20 Elementen à 4 int Werten ist, mag die Kopie gehen

    Warum ueberhaupt Zeiger ... Referenzen, RAII, ...



  • Ka55i0peia schrieb:

    Das ist mir schon klar. Die Frage war, ob man einem Pointer "ansieht", dass sein Bereich auf den er zeigt bereits ungültig ist. So wie ich das sehe, geht das nicht so ohne weiteres.

    Nein, wenn du das willst, hast du naemlich ein Design Problem.

    Eigentlich kann man das naemlich schon nachsehen, aber wie gesagt: das ist keine Loesung fuer dein Problem.

    Ergo, ist die modellierung der Speicherverwaltung in diesem Programmteil mist. Trotzdem vielen Dank für die Hinweise.

    http://bash.org/?866112 schrieb:

    <glyph> For example - if you came in here asking "how do I use a jackhammer" we might ask "why do you need to use a jackhammer"
    <glyph> If the answer to the latter question is "to knock my grandmother's head off to let out the evil spirits that gave her cancer", then maybe the problem is actually unrelated to jackhammers



  • Ka55i0peia schrieb:

    Das ist mir schon klar. Die Frage war, ob man einem Pointer "ansieht", dass sein Bereich auf den er zeigt bereits ungültig ist. So wie ich das sehe, geht das nicht so ohne weiteres.

    Das siehst du richtig. Ein Zeiger ist nichts weiter als ein Integerwert in dem die Adresse eines Objektes steht. Und es gibt keine Möglichkeit diese auf gültigkeit zu überprüfen, da nirgens Statusinformationen zu den Adressen hinterlegt werden. In C sowie in C++ zahlt man nur für das, was man braucht (=> möglichst geringer Overhead und möglichst geringe Laufzeit).

    Dafür sind Smartpointer die richtige Wahl (Oder z.B. die Pointercontainer von Boost), wobei man durchaus prüfen sollte, welchen Overhead sie mit sich bringen (Man sollte den shared_ptr nicht leichtfertig nutzen - aber es gibt Stellen, wo er einfach die sinnvollste Wahl ist).



  • Das erste update wird sich also um das Design der Speicherverwaltung drehen...

    Vielen Dank für die raschen & hilfreichen Antworten!
    (Der Post ist hiermit beendet.)



  • Smart-Pointers könnten auch eine externe kennichmap zum mitzählen nehmen.

    std::map<int*,size_t> kennich; 
    
    int *i = new int;++kennich[i]; 
    int *p = i;++kennich[p]; 
    
    delete i;--kennich[i]; 
    
    if(kennich[p]){ 
      delete p;--kennich[p]; 
    }
    

    Übrigens hat eine Hashtable, die solche Zeiger als int interpretiert und mit der allseits beliebten irrationalsten Zahl multipliziert bevor die letzten paar Bits als Index genommen werden, die starke Tendenz, überhaupt keine Kollissionen zu bekommen.



  • volkard schrieb:

    Smart-Pointers könnten auch eine externe kennichmap zum mitzählen nehmen.

    Aber bitte nicht wie in deinen Beispiel 😉

    Du löschst einen Zeiger, obwohl noch ein anderer Zeiger auf diesen verweist, und anschließend löschst du ihn wieder (Inhalt von i & p sind identisch).

    Ich zerlege mal dein Beispiel in Einzelzeilen:

    std::map<int*,size_t> kennich; 
    
    int *i = new int; // Nehmen wir mal an i verweist auf adresse 0xA0
    ++kennich[i];     // kennich[0xA0] == 1
    
    int *p = i;       // p verweist nun auch auf 0xA0
    ++kennich[p];     // kennich[0xA0] == 2
    
    delete i;         // Der Speicherbereich 0xA0 wird gelöscht (obwohl noch
                      // in Verwendung).
    --kennich[i];     // kennich[0xA0] == 1
    
    if(kennich[p]) {  // kennich[0xA0] == 1...
        delete p;     // Der Speicherbereich 0xA0 wird versucht nochmals zu
                      //   löschen... => Undefiniert
        --kennich[p]; // kennich[0xA0] == 0
    }
    

    Eher müsstest du vor dem Delete runterzählen, und löschen wenn der Zähler dann 0 ist...

    std::map<int*,size_t> kennich; 
    
    int *i = new int; // Nehmen wir mal an i verweist auf adresse 0xA0
    ++kennich[i];     // kennich[0xA0] == 1
    
    int *p = i;       // p verweist nun auch auf 0xA0
    ++kennich[p];     // kennich[0xA0] == 2
    
    --kennich[i];     // kennich[0xA0] == 1
    if(!kennich[i])   // Noch in Gebrauch, daher nicht löschen
        delete i;
    
    --kennich[p];     // kennich[0xA0] == 0
    if(!kennich[p])   // Nicht mehr in Gebrauch, also löschen
        delete p;     // ...
    


  • asc schrieb:

    Aber bitte nicht wie in deinen Beispiel 😉

    Uups. Huih, habe ich da einen Mist gebastelt. Anscheinen kann ich mich in drei gut laufenden Tagen leerprogrammieren.



  • volkard schrieb:

    Uups. Huih, habe ich da einen Mist gebastelt.

    Passiert, sonst bräuchte man ja auch keine Qualitätssicherung und die Softwarewelt wäre fehlerfrei. Und auch Moderatoren sind auch nur Menschen...


Log in to reply