Ampersand and Pointer (Referenz und Zeiger) - Verständnis-Frage



  • Hallo Zusammen,

    meine nächste Frage dreht sich um den Unterschied zwischen Referenz und Zeiger. Ich bin in meinem Lehrbuch bei Pointern angekommen und sehe aktuell noch keinen Vorteil zu einer Referenz bzw. geeigneteren Einsatzzwecken.
    Ich verstehe es gemäß Buch aktuell so: Eine Referenz ist nur ein weiterer Alias-Name für das eigentliche Objekt. Ein Pointer enthält die Adresse des Objektes.
    Meine eigentliche Frage kommt jetzt: Wann verwende ich bevorzugt was und warum - könnt ihr mir hierfür ein paar logische Beispiele und Erklärungen geben?

    Ich habe unten ein Beispiel angehängt in welchem ich teils aus dem Buch und teils selbst die Funktionalität teste. Und außer direkt die Adresse ausgeben zu können, sehe ich noch keinen Einsatzzweck für Pointer 🙂

    Ergänzung: Ich hab noch einen Pointer auf die Referenz ref hinzugefügt um zu beweisen, dass es sich wirklich um einen Alias-Namen und keine Kopie handelt. Da die Adresse identisch zu der Adresse von i (Pointer ip auf i) ist, sehe ich es als bestätigt an.

    Danke euch, Beste Grüße,
    Tommy

    #include <iostream>
    
    int main()
    {
    	struct Struktur {
    		int x;
    		int y;
    	};
    
    	Struktur s{ 4, 9 };
    	std::cout << "Structure: " << s.x << " " << s.y << '\n';
    		Struktur* sptr{ &s }; 
    	std::cout << "Pointer: " << sptr->x << " " << sptr->y << '\n';
    	Struktur& sref{ s };
    	std::cout << "Reference: " << sref.x << " " << sref.y << "\n\n";
    
    	int i{ 99 };
    	int& ref{ i };
    	int* ip{ &i };
    	int* refp{ &ref };
    	std::cout << "Initalized values:\n";
    	std::cout << "int i: " << i << " int& ref{i}: " << ref << " int* ip{&i}: " << ip << " int* refp{&ref}: " << refp << '\n';
    
    	*ip = 55;
    	std::cout << "*ip = 55\n";
    	std::cout << "int i: " << i << " int& ref{i}: " << ref << " int* ip{&i}: " << ip << " int* refp{&ref}: " << refp << '\n';
    
    	ref = 66;
    	std::cout << "ref = 66\n";
    	std::cout << "int i: " << i << " int& ref{i}: " << ref << " int* ip{&i}: " << ip << " int* refp{&ref}: " << refp << '\n';
    
    	return 0;
    }
    

    Output:
    Structure: 4 9
    Pointer: 4 9
    Reference: 4 9

    Initalized values:
    int i: 99 int& ref{i}: 99 int* ip{&i}: 00BAF7CC int* refp{&ref}: 00BAF7CC
    ip = 55
    int i: 55 int& ref{i}: 55 int
    ip{&i}: 00BAF7CC int* refp{&ref}: 00BAF7CC
    ref = 66
    int i: 66 int& ref{i}: 66 int* ip{&i}: 00BAF7CC int* refp{&ref}: 00BAF7CC



  • Es geht bei dieser Frage einmal um Parameterübergabe und wie Objekte (auf dem Stack oder auf dem Heap) anlegt werden.
    Als Parameter kann ein Pointer auch NULL (nullptr) sein wogegen eine Referenz eigentlich immer gesetzt sein muss.
    Wenn ich mich entscheide, Objekte auf dem Heap anzulegen, weil ich ggf. gar nicht weiß, wie viele es werden (der Stack ist begrenzt!), gebe ich diese so nicht als Parameter weiter, sondern nutze strickt Smartpointer. So muss ich mich nicht um die Speicherverwaltung kümmern (new und delete).
    Sollten Objekte "klein" sein und können kopiert werden, sollte man dieses ggf. auch nutzen.

    Edit: Sehe gerade, Ähnliches wurde schon mal diskutiert -> https://www.c-plusplus.net/forum/topic/231254/pointer-vs-referenz



  • @Helmut-Jakoby Hi Helmut, danke für die Antwort. Da ist viel Neues für mich dabei und verstehe es nur bedingt. Stack, Heap und Smartpointer musste ich erstmal nachlesen. Verstehe ich es richtig, dass du dann nie normale Pointer verwendest sondern immer nur Smartpointer?

    Die Diskussion in dem alten Thread habe ich nach der Hälfte abgebrochen. Das scheint mir dann doch sehr subjektiv zu sein, wie Referenzen und Zeiger eingesetzt werden. So eine klare Linie wird es dann wohl nicht geben und ich bin wohl nicht der erste der die Fragen stellt :).



  • Meine "Meinung"

    • Smartpointer wenn es darum geht Ownership zu übergeben, oder irgendwo zu haben
    • Referenzen wenn möglich
    • Pointer wenn's nicht anders geht (z.B. nullptr eine Möglichkeit ist).

    Siehe auch: https://isocpp.org/wiki/faq/references#refs-vs-ptrs



  • @Helmut-Jakoby sagte in Ampersand and Pointer (Referenz und Zeiger) - Verständnis-Frage:

    Wenn ich mich entscheide, Objekte auf dem Heap anzulegen, weil ich ggf. gar nicht weiß, wie viele es werden (der Stack ist begrenzt!), gebe ich diese so nicht als Parameter weiter, sondern nutze strickt Smartpointer.

    Ein furchtbar komischer Rat. Wenn ich einen Container für Objekte brauche dann nehme ich den passenden Container für Objekte. Und an Funktionen die auf Objekten dieses Containers operieren übergebe ich Iteratoren. Neuerdings soll es auch Ranges geben.



  • @Kvothe sagte in Ampersand and Pointer (Referenz und Zeiger) - Verständnis-Frage:

    Verstehe ich es richtig, dass du dann nie normale Pointer verwendest sondern immer nur Smartpointer?

    Es geht um Ownership (= wer ist dafür verantwortlich den Dreck wieder wegzuräumen). Ein Smartpointer hat in der Regel ownership. Dh. wenn nichts und niemand mehr auf das Objekt verweist das er besitzt, so räumt er das Objekt weg sobald er out of scope geht.
    Raw (= rohe) pointers sollten niemals ownership haben weil man sich dann ums wegräumen der Ressource selbst kümmern muss (das mag auf den ersten Blick noch nicht so tragisch anmuten aber spätestens bei Exceptions hört dann der Spaß auf, siehe Exception Safety). Bei einem Smartpointer macht das dessen Destruktor.



  • @Kvothe sagte in Ampersand and Pointer (Referenz und Zeiger) - Verständnis-Frage:

    meine nächste Frage dreht sich um den Unterschied zwischen Referenz und Zeiger.

    Wir müssen hier ja bei den Basics bleiben, bevor hier weiterführende Dinge kommen.

    Referenz: die MUSST du immer initialisieren, sodass sie dann gleich damit verknüpft werden kann. Danach kannst du die Referenz selber nicht mehr auf eine andere Variable umsetzen, denn jede Zuweisung an die Referenz änder ja die referenzierte Variable.

    Pointer: du kannst einen Pointer auch auf "gar nichts" (den nullptr) zeigen lassen oder im Verlaufe deines Codes den Pointer auf andere Dinge zeigen lassen. D.h. du kannst sowohl den Pointer selbst als auch den Wert, auf den der Pointer zeigt, verarbeiten/ändern.

    Wenn du zum Beispiel eine verkettete Liste hast (kommt in deinem Lehrbuch bestimmt auch, ansonten lies z.B. bei Wikipedia nach), dann brauchst du einen Zeiger auf den Nachfolger, da du da variabel sein musst.

    Zusätzlich kannst du Zeiger nutzen, um sie auf mit new erzeugste Objekte im Freispeicher zeigen zu lassen. Das ist das, was man NICHT tun sollte, wo man stattdessen Containertypen oder Smartpointer einsetzen sollte.


Log in to reply