Objectpointer



  • Hallo,

    ich komme von Sprachen mit GC und sehe noch nicht den riesigen Unterschied.

    Bsp

    #include <iostream>
    using namespace std;
    
    class Test {
    	int a;
    public:
    	int getA() {
    		return a;
    	}
    
    Test(): a(1){}
    Test(int i): a(i){}
    
    };
    
    int main() {
    	Test t1(100);
    	Test *t2 = new (nothrow) Test(100);
    	cout << sizeof(t1) << " " << sizeof(t2) << endl; // prints !!!Hello World!!!
    	delete t2;
    	return 0;
    }
    

    t1 scheint auch ein pointer zu sein, genau so wie t2. Wo genau liegt der unterschied zwischen t1 und t2?

    Warum muss ich bei t2 manuell den Speicher freigeben wenn ich doch auf den destructor zugreifen kann, oder wird einfach nur der destructor gecalled wenn ich delete auf t2 mache?



  • t1 ist kein Pointer.
    t1 ist eine Instanz der Klasse Test.
    Hier in C++ sagen wir, t1 ist die Instanz, obwohl t1 eigentlich eine Referenz auf die Instanz ist. Diese Referenz ist "für immer" an die Instanz gebunden, und Referenzen können niemals nichts referenzieren, d. h. wenn du eine Referenz benutzt dann kannst du dir sicher sein dass die Referenz immer ein gültiges Objekt referenziert - im Gegensatz zu Pointern 😉

    t1 ist eine sog. automatische Variable, das heißt, ihr Speicher wird automatisch vom Stack allokiert und wieder freigegeben, außerdem werden auch ihre Konstruktoren und Destruktoren automatisch aufgerufen (Ausnahmen gibt es, sind aber hier irrelevant).



  • Und: ja, beim freigeben von Speicher mit delete werden automatisch die Destruktoren der entsprechenden Objekte aufgerufen.



  • Okay danke 🙂



  • Sone schrieb:

    [...] t1 ist die Instanz, obwohl t1 eigentlich eine Referenz auf die Instanz ist. Diese Referenz ist "für immer" an die Instanz gebunden, und Referenzen können niemals nichts referenzieren, d. h. wenn du eine Referenz benutzt dann kannst du dir sicher sein dass die Referenz immer ein gültiges Objekt referenziert - im Gegensatz zu Pointern 😉

    WTF?



  • Tipp: Hör nie auf Sone, der erzählt ständig irgendwelchen Mist und hat keine Ahnung.

    t1 ist tatsächlich die Instanz, mit Referenz hat das Ganze nichts zu tun. Noch dümmer ist allerdings die Aussage, dass Referenzen im Gegensatz zu Zeigern immer gültig seien (siehe out of scope Objekte).



  • Zeig' mir bitte ein Beipiel, wo das referenzierte Objekt out of scope geht und die Referenz überlebt ...



  • Hier zum Beispiel:

    int& foo()
    {
      int a=42;
      return a;
    }
    


  • Nur zur Klarstellung: Ich bin nicht "Wie oft denn noch?".

    Aus http://www.c-plusplus.net/forum/309901:

    Sone schrieb:

    seriöseantwort schrieb:

    Sone schrieb:

    Bugs entstehen nie durch Referenzen an sich

    Gib mal eine Referenz auf ein lokales Objekt zurück.

    👍 Gutes Beispiel (ehrlich :D)

    In dem Thread hat sich Sone wieder einmal durch seine Unkenntnis (dort in Bezug auf Referenzen) auf sich aufmerksam.

    Aber sein letzter Post übertrifft wirklich alles.

    Sone schrieb:

    Hier in C++ sagen wir, t1 ist die Instanz, obwohl t1 eigentlich eine Referenz auf die Instanz ist. Diese Referenz ist "für immer" an die Instanz gebunden, und Referenzen können niemals nichts referenzieren, d. h. wenn du eine Referenz benutzt dann kannst du dir sicher sein dass die Referenz immer ein gültiges Objekt referenziert - im Gegensatz zu Pointern

    😮



  • Athar schrieb:

    Hier zum Beispiel:

    int& foo()
    {
      int a=42;
      return a;
    }
    

    *bin c++ beginner*
    Wird a nicht sofort zerstört nach dem foo zu ende ist? Also ist die Referenz zu a ungültig nach Funktionsaufruf?
    Edit:
    Habe es gerade gestestet und es geht anscheinend, sehr merkwürdig.

    Zu meiner eigentlichen Fragen.
    Also benutze ich eigentlich nur Instanzen und kümmere mich um con/destructors?



  • kantaki schrieb:

    Habe es gerade gestestet und es geht anscheinend, sehr merkwürdig.

    Klassisches UB ➡ Undefined Behaviour, "es geht" zufällig, aufgrund der Compiler Implementation und den Umständen wie der Code geschrieben ist. Kurz: Es geht nicht, weil es laut Standard nicht gehen darf. Es ist schlicht falscher Code.



  • kantaki schrieb:

    :::
    
    class Test {
    :::
    };
    
    int main() {
       Test t1(100);
       Test *t2 = new (nothrow) Test(100);
       cout << sizeof(t1) << " " << sizeof(t2) << endl; // prints !!!Hello World!!!
       delete t2;
       return 0;
    }
    

    t1 scheint auch ein pointer zu sein, genau so wie t2. Wo genau liegt der unterschied zwischen t1 und t2?

    Nein, wie kommst du darauf? t1 ist kein Zeiger. t1 ist ein Test-Objekt. t2 ist ein Zeiger auf ein Test-Objekt. Das erste Test-Objekt lebt im automatischem Speicher und lebt nur solange, bis die Funktion zu Ende ist, weil die Dinger im automatischen Speicher automatisch weggeräumt werden. Das zweite Test-Objekt lebt im Freispeicher, weil du es mit new angelegt hast. Würdest du dieses Objekt nicht per delete wieder löschen, würde es noch im Speicher hängen, bis das Programm komplett beendet ist. Der Zeiger t2 lebt aber wiederum im automatischen Speicher. Der wird ordnungsgemäß und automatisch weggeräumt. Nur passiert da nicht viel, wenn ein roher Zeiger weggeräumt wird.

    kantaki schrieb:

    Warum muss ich bei t2 manuell den Speicher freigeben wenn ich doch auf den destructor zugreifen kann, oder wird einfach nur der destructor gecalled wenn ich delete auf t2 mache?

    Bitte was? Du musst das Ding manuell wegräumen, weil du es im Freispeicher angelegt hast. Dazu ist der Freispeicher da. Er überlässt dir die Kontrolle über die Lebenszeit von Objekten.



  • delete ruft den destructor auf und gibt den Speicher frei. Ein destructor-Aufruf gibt aber nicht den Speicher des Objektes (von dessen destructor wir sprechen) frei.



  • Test t1;
    Test* t2
    

    t2 ist vom Typ Pointer und ist meist 4 oder 8 Byte gross (compilerabhaengig). Zeiger zeigen fuer gewoehnlich auf etwas. Hier auf ein Objekt, das mit new erzeugt wurde. Alles was mit new erzeugt wurde, muss (sollte) mittels delete geloescht werden. Um das Objekt zu identifizieren wird der Zeiger t2 benutzt. Der Zeiger t2 ist nach dem delete noch genauso vorhanden wie vorher und zeigt immer noch auf die gleiche Stelle. Da ist aber kein Testobjekt mehr.

    Warum muss ich bei t2 manuell den Speicher freigeben wenn ich doch auf den destructor zugreifen kann

    t2 ist ein Zeiger. Der hat keinen Destruktor. Genau wie: eine Hand mag auf ein Flugzeug zeigen, ist deswegen aber noch lange kein Flugzeug.

    Btw. Der Beitrag von Sone ist jetzt nicht so daneben.



  • okay danke, ich dachte c++ würde mir hier impliziert einen pointer geben. Deswegen habe ich sizeof() benutzt. t1 und t2 waren beide 4, also einfach angenommen das beide vom typ pointer sind.



  • knivil schrieb:

    Btw. Der Beitrag von Sone ist jetzt nicht so daneben.

    Also ist t1 eine Referenz?



  • knivil schrieb:

    Btw. Der Beitrag von Sone ist jetzt nicht so daneben.

    Also können Referenzen immer nur gültige Objekte referenzieren?



  • Wie oft denn noch? schrieb:

    knivil schrieb:

    Btw. Der Beitrag von Sone ist jetzt nicht so daneben.

    Also können Referenzen immer nur gültige Objekte referenzieren?

    Du hast es kapiert. 👍

    Swordfish schrieb:

    knivil schrieb:

    Btw. Der Beitrag von Sone ist jetzt nicht so daneben.

    Also ist t1 eine Referenz?

    Für den Compiler macht das null Unterschied.
    P.S.: Ist es natürlich nicht. Aber man kann es als Referenz betrachten, weil t1 intern, genau wie eine Referenz, nur ein konstanter Zeiger mit Syntaxzucker ist, und es eben syntaktisch keinen Unterschied macht.



  • Ich verstehe nicht, wieso Referenzen nur auf gültige Objekte referenzieren können. Eigentlich sind die doch genau wie Zeiger, nur dass gilt, dass bei Definition auch Initialisierung geschehen muss (<- keine Ahnung, ob das jetzt nach Standard streng genug definiert ist).

    Folgende Referenzen auf ungültige Objekte funktionieren doch syntaktisch:

    int* ptr = new int(5);
    int& ref1 = *ptr;
    
    delete ptr; // nun zeigt refl auf kein gültiges Objekt mehr
    

    Zweitens hat t2 doch eine Indirektion, die t1 nicht hat oder nicht? Geschieht Zugriff auf den Stack nicht schneller? Oder begründet sich der schnellere Zugriff darauf, dass der aktuelle Stackbereich eben "näher" im Vergleich zum Freispeicher und garantiert cachelokal ist?



  • Sone schrieb:

    Wie oft denn noch? schrieb:

    Also können Referenzen immer nur gültige Objekte referenzieren?

    Du hast es kapiert. 👍

    Swordfish schrieb:

    Also ist t1 eine Referenz?

    Für den Compiler macht das null Unterschied.
    P.S.: Ist es natürlich nicht. Aber man kann es als Referenz betrachten, weil t1 intern, genau wie eine Referenz, nur ein konstanter Zeiger mit Syntaxzucker ist, und es eben syntaktisch keinen Unterschied macht.

    Sone, wie lernresistent kann man eigentlich sein? Deine Aussagen wurden dermassen widerlegt, dass sie nie mehr auch nur annähernd wahr werden 🙄


Anmelden zum Antworten