Instanz exestiert nach Aufruf des Destruktors immer noch!



  • Wird der Speicher nicht einfach bloß freigegeben also "unlocked"? Das muss ja nicht zur Folge haben, dass er auch gleich überschrieben wird... Demzufolge funktionieren verwaiste Zeiger immer noch. Allerdings gibt es nach der Speicherfreigabe keine Garantie mehr, dass der Speicher nicht überschrieben wird....

    Ausserdem redet einer von statischer Reservierung auf dem Stack und ein anderer von dynamischer Allokierung auf dem Heap....
    Den Pointer, der auf den dynamisch Allokierten Speicherbereich zeigt, zu NULLen würde nur ein MemoryLeak verursachen..... Man muss den Speicher mit "delete" freigeben.

    Das {}-Paar setzt eine Stackframe (richtiger Begriff ? 😮 ) auf. Nach Ende des {}-Paars wird der Speicher freigegeben....

    Compiler reservieren Stack mit statischer Addressauflösung:

    // Stackgröße ist 0Bytes also Stackpointer 0x00000000
      dog* pdog;   
      {                  // erzeugen neuer Stackframe
        dog donald;      // Stack erhöht durch sizeof(class dog), Stackpointer+=sizeof(class dog)
        pdog = &donald;  // zeigt auf 0x00000000, also den Anfang des Stacks, wo auch das Objekt liegt
        donald.setage(5);  
        cout << donald.getage() << endl;  
        //donald.~dog(); 
      }//dtor            // Stackframe wird aufgelößt, Stackpointer wieder 0x00000000
                         // der Stack ist aber noch nicht überschrieben und pdog, nun ein verwaister Zeiger, welcher immernoch auf den
                         // Speicher des Objekts zeigt. Dieser kann jedoch Jederzeit überschrieben werden...
      cout << pdog->getage() << endl; 
      dog wuffi;         // Da der Stack jetzt wieder am Anfang steht, wird der alte Inhalt überschrieben...
      wuffi.setage(9);  
      cout << wuffi.getage() << endl;  
      cout << pdog->getage() << endl; // ... und jetzt? 
    
      dog waffi;  
      waffi.setage(13);  
      cout << waffi.getage() << endl;  
      cout << pdog->getage() << endl; // ... und jetzt? 
    
      getch(); 
      return 0; // nach ISO-C++-Standard (1998) überflüssig  
    }
    

    (wäre meine Begründung, erklärt auch das im 1.Fall nicht überschrieben wird)

    wenn mein Senf stimmt, müsste

    // Stackgröße ist 0Bytes also Stackpointer 0x00000000
      dog* pdog;   
      {                  // erzeugen neuer Stackframe
        dog donald;      // Stack erhöht durch sizeof(class dog), Stackpointer+=sizeof(class dog)
        pdog = &donald;  // zeigt auf 0x00000000, also den Anfang des Stacks, wo auch das Objekt liegt
        donald.setage(5);  
        cout << donald.getage() << endl;  
        //donald.~dog(); 
      }//dtor            // Stackframe wird aufgelößt, Stackpointer wieder 0x00000000
                         // der Stack ist aber noch nicht überschrieben und pdog, nun ein verwaister Zeiger, welcher immernoch auf den
                         // Speicher des Objekts zeigt. Dieser kann jedoch Jederzeit überschrieben werden...
    
      char buff[sizeof(class dog)]="";      // Stack reservieren
                                            // Wird der Stack bloß reserviert und nicht überschrieben, kann der verwaiste Zeige 
                                            // bis zum Programmende seine Funktionalität behalten..         
      memset(buff,' ', sizeof(class dog) ); // Stack überschrieben
    
      cout << pdog->getage() << endl; 
      dog wuffi;         // Da der Stack jetzt wieder am Anfang steht, wird der alte Inhalt überschrieben...
      wuffi.setage(9);  
      cout << wuffi.getage() << endl;  
      cout << pdog->getage() << endl; // ... und jetzt? 
    
      dog waffi;  
      waffi.setage(13);  
      cout << waffi.getage() << endl;  
      cout << pdog->getage() << endl; // ... und jetzt? 
    
      getch(); 
      return 0; // nach ISO-C++-Standard (1998) überflüssig  
    }
    

    Die beiden Zeilen dürften bewirken, das donald "tot" ist 😃

    Lange Rede kurzer Sinn.... : Der Speicher wird freigegeben aber nicht automatisch überschrieben/gelöscht ( gilt für Heap und Stack )mit 0000000 oder so.... Er steht also für neue Reservierungen zur Verfügung

    gruß



  • donald.~dog(); // hund muss jetzt tot sein 
    cout << donald.getage(); // geht immer noch!!!! Warum zum teufel?????
    

    Dies war der Ausgangspunkt. Es verwunderte den unbedarften Nutzer eben, dass das "Objekt" (nicht nur der Speicherbereich mit seinen noch nicht überschriebenen Inhalten) noch existiert und über seinen Namen ohne Compilerwarnung ansprechbar ist. Der Destruktor wird bei '}' nochmals aufgerufen.

    Aber mal eine ganz andere Frage: wie kann man unterscheiden/feststellen, ob ein Objekt "lebendig" oder nur noch ein "Zombie" ist? 😃



  • puh kenn ich nicht....

    Unter Windows kann man mit IsBad*-Calls abfragen ob man von Addresse lesen darf... Aber von der Addresse kann man ja trotzdem noch lesen... wie gesagt sorry kA

    gruß



  • Gibt es da wirklich keine Möglichkeit zu unterscheiden, ob ein Objekt "lebendig" oder nur noch ein "Zombie" ist? 😕



  • Erhard Henkes schrieb:

    Gibt es da wirklich keine Möglichkeit zu unterscheiden, ob ein Objekt "lebendig" oder nur noch ein "Zombie" ist? 😕

    nein, wozu auch?

    es gibt in C++ keine Zombie-Objekte
    den dtor darf man nur im zusammenhang mit placement new aufrufen.



  • "es gibt in C++ keine Zombie-Objekte."

    In der Theorie vielleicht nein, in der Praxis aber wohl (s.o.).
    Wir haben hier bisher vor allem über Objekte auf dem Stack (also ohne new erzeugt) gesprochen, nicht auf dem heap. Die kann man durch einen "destructor praecox" in so eine Art "zombies" verwandeln. Nun geht es um die Detektion dieser Objektspezies. Ein lustiges Thema. 😃



  • wo ist da n zombie objekt?

    donald.~dog();
    darfst du eingach nicht machen - hoechstens um nachher ein placement new anzubringen - aber selbst da wuerde ich stark an dem programmierer zweifeln.

    es ist zwar moeglich ein objekt zum zombie zu machen - doch wer das tut ist selber schuld - denn es ist nicht vorgesehen.



  • Definiere Zombie.

    Wir haben hier bisher vor allem über Objekte auf dem Stack (also ohne new erzeugt) gesprochen, nicht auf dem heap. Die kann man durch einen "destructor praecox" in so eine Art "zombies" verwandeln. Nun geht es um die Detektion dieser Objektspezies. Ein lustiges Thema

    Laut C++ Lebenszyklus-Modell existiert mit Betreten des Destruktors kein Objekt mehr. Es existiert nur noch ein haufen toter Bits. Keine Ahnung ob das deiner Definition von Zombie gleichkommt. Erkennen kannst du diese Objektspezie freilich nicht, da hier keine Objekte mehr existieren.
    Daran würde übrigens auch das Ausnullen des Speichers nichts ändern.

    Man darf nicht den Namen eines Objekts (den genauen Standard-Begriff weiß ich im Moment nicht) oder die Referenzen auf ein Objekt mit dem Objekt selbst verwechseln. Der Destruktor zerstört ein Objekt. Der Name verliert mit dem Ende des Scopes seine Gültigkeit. Das eine hat mit dem anderen aber nicht direkt etwas zu tun.
    Bei auto-Objekten fällt beides im Normalfall zusammen. Mit Scope-Ende verschwindet also der Name und es wird der Dtor des Objekts aufgerufen.

    Wenn du vorher selbst den Dtor aufrufst, existiert wie gesagt kein Objekt mehr. Der Name referenziert danach kein Objekt, sondern toten Speicher.

    Wie auch immer. Für solche Sachen braucht man keine Objekte im C++-OOP-Sinne. Das geht genauso gut mit C und Zeigern.



  • Wie kann man unterscheiden, ob ein "Name" (ist ja ein zeiger) ein gültiges Objekt oder einen toten Bithaufen, der nur noch ein ungültiges Abbild des Objektes ist, adressiert?



  • Wie kann man unterscheiden, ob ein "Name" (ist ja ein zeiger) ein gültiges Objekt oder einen toten Bithaufen, der nur noch ein ungültiges Abbild des Objektes ist, adressiert?

    Gar nicht. Wie ich oben bereits schrieb.



  • Thomas Strasser, C++ Programmieren mit Stil, 2. Auflage, 2003, S.187:
    "Elementfunktionen, die automatisch aufgerufen werden (wie etwa Konstruktor, Destruktor etc.), sollten nicht von Hand aus aufgerufen werden."

    Thomas Strasser, C++ Programmieren mit Stil, 2. Auflage, 2003, S.196:
    "Konstruktoren und Destruktoren sind Initialiserungs- bzw. De-Initialiserungsfunktionen. Sie sind nicht verantwortlich für das Anlegen bzw. Freigeben des Objekts selbst bzw. dessen Instanzvariablen. Das wird vom C++-System übernommen."



  • "Konstruktoren und Destruktoren sind Initialiserungs- bzw. De-Initialiserungsfunktionen. Sie sind nicht verantwortlich für das Anlegen bzw. Freigeben des Objekts selbst bzw. dessen Instanzvariablen. Das wird vom C++-System übernommen."

    Besser: Sie sind nicht verantwortlich für das Anlegen bzw. Freigeben des vom Objekt verwendeten Speichers. Das wird vom operator new bzw. operator delete erledigt.



  • new/delete gilt für Objekte auf dem Heap. Es gibt aber auch Objekte auf dem Stack oder im globalen Speicher (oder sonst wo).



  • Erhard Henkes schrieb:

    new/delete gilt für Objekte auf dem Heap. Es gibt aber auch Objekte auf dem Stack oder im globalen Speicher (oder sonst wo).

    ja, aber das system ist das gleiche.

    {
    int i; //i wird allokiert
    } //i wird deallokiert

    deswegen auch meine beispiele mit new - denn der mechanismus ist der gleiche.



  • Ich verstehe die ganze Aufregung gar nicht. Es ist doch ganz klar, daß es Zombies geben kann, man könnte die wohl auch durch ein hartes Zeigercasting wieder reaktivieren (allerdings mit den bei Zombies üblichen Nebenwirkungen - holt schon mal ein Kreuz).

    Und der Unterschied zwischen Zombies auf dem Stack und auf dem Heap ist doch auch sonnenklar.

    Aber das liegt an der technischen Grundlage der Speicherverwaltung von C++ und dem OS. Und es stellt keine wie auch immer geartete Einschränkung für den Entwickler dar.

    Ich sehe ein, daß jemand beim ersten Mal verwundert ist, aber wieso kann dieser Thread so viele Beiträge bekommen? 😕


Anmelden zum Antworten