Pointer vs. Referenz



  • Ich kenne beides sehr genau, aber oft weiß ich trotzdem nicht, wie ich z.B. einen Parameter in einer Funktion deklarieren soll: Als Pointer oder Referenz? Hässlich finde ich es, wenn sich beides vermischt. Angenommen ich entwickle eine Klasse und stelle dann fest, dass icih mit Referenzen irgendwas nicht implementieren kann, sondern eher einen Zeiger benötige - dann habe ich ein inkosistentes Interface oder zumindest alle anderen Methoden umzuschreiben.

    Gibt es da so etwas wie ein empfohlene Vorgehensweise? Referenzen wann immer es möglich ist, Pointer nur wenn es nicht anders geht? Auf Referenzen ganz verzichten und nur auf Zeiger setzen?

    Und noch eine Frage: Wird sich beim jetzigen Referenz-Menchanismus etwas mit C++0x ändern, was das ungebunden-sein betrifft? Über RValue-Referenzen weiß ich schon bescheid.

    Danke im Voraus.



  • Also Referenzen sind ja im Pinzip Pointer die nicht NULL sein dürfen. Also würde ich immer da eine Referenz als Parameter übergeben, wo auch wirklich ein Objekt benötigt wird. Wenn du als Parameter nicht unbedingt ein Objekt brauchst, dann kannst du auch nen Zeiger übergeben (und dann auf NULL prüfen).

    In Kopierkonstruktoren auch immer Referenzen nutzen.



  • Chris++ schrieb:

    Also Referenzen sind ja im Pinzip Pointer die nicht NULL sein dürfen. ...

    UND die nicht "umgehängt" werden können!
    Bisweilen ist ein Zeiger einfach deshalb notwendig, weil man mal auf dieses und mal auf jenes Objekt verweisen will.

    Gruß,

    Simon2.



  • Lets fight! schrieb:

    Ich kenne beides sehr genau, aber oft weiß ich trotzdem nicht, wie ich z.B. einen Parameter in einer Funktion deklarieren soll: Als Pointer oder Referenz?

    Prinzipiell kann man ja in beiden Fällen in der aufgerufenen Funktion den Wert des Parameters verändern. Wenn man nun den Code liest und verstehen will, ist das an der Stelle, wo der Aufruf erfolgt, dann nicht erkennbar, wenn man Referenzen benutzt, weil der Aufruf mit einer Referenz sich nicht von einem Aufruf mit einem Wert unterscheidet.
    Deshalb ist es meiner Meinung eine gute Idee, dort Zeiger zu nehmen, wo man in der aufgerufenen Funktion wirklich den übergebenen Parameter verändert; sieht man dann im Code einen Aufruf mit einem Zeiger, kann man davon ausgehen, daß der übergebenen Parameter nach Funktionsaufruf verändert ist.
    Dort, wo man in der Funktion den Wert nicht verändert, kann man dann eine konstante Referenz als Parameter nutzen.

    Das hat wie gesagt den Vorteil, daß ich beim Code lesen schon weiß, daß ein Aufruf wie machwas(hiermit) sich von machwas(&hiermit) so unterscheidet, daß ich weiß, ob mein Parameter von machwas evtl. geändert wird.



  • Belli schrieb:

    Prinzipiell kann man ja in beiden Fällen in der aufgerufenen Funktion den Wert des Parameters verändern. Wenn man nun den Code liest und verstehen will, ist das an der Stelle, wo der Aufruf erfolgt, dann nicht erkennbar, wenn man Referenzen benutzt, weil der Aufruf mit einer Referenz sich nicht von einem Aufruf mit einem Wert unterscheidet.

    Die Diskussion hatten wir erst vor wenigen Tagen ausführlich.
    Man kann auch an einen Zeiger nicht sehen ob dieser Modifiziert wird oder nicht (Manchmal benötigt man Zeiger auch wengen der zulässigkeit von NULL etc., ohne das es modifizierend ist).
    Ich sehe dieses Argument daher als unbrauchbar an (zumindestens für die Auswahl von Referenzen vs. Zeigern).

    Wirklich erkennen kann man nur das, was in der Deklaration angegeben wird (meist zeigen das Moderne IDEs eh beim tippen an).

    cu André



  • asc schrieb:

    Die Diskussion hatten wir erst vor wenigen Tagen ausführlich.
    Man kann auch an einen Zeiger nicht sehen ob dieser Modifiziert wird oder nicht (Manchmal benötigt man Zeiger auch wengen der zulässigkeit von NULL etc., ohne das es modifizierend ist)

    Sorry, diese Diskussion habe ich nicht mitbekommen. Ich bleibe dabei, wenn ich Referenzen immer als const übergebe, und nur in Fällen, wo ich ändern können muß, Zeiger benutze (eventuell noch, wenn ich 0(NULL) übergeben können muß), erleichtert mir das die Lesbarkeit, weil ich mich dann auf bestimmte Dinge verlassen kann.
    Die NULL-Zeiger - Geschichte kann man evtl. anders lösen (Überladung), aber selbst wenn nicht, weiß ich, wenn ich das konsequent durchziehe, daß ich nicht von Parameter-Änderungen überrascht werden kann, wenn ich nicht eine Adresse übergebe.
    Wer dieser Argumentation nicht folgen mag, nehme wo immer möglich Referenzen, weil sie in der aufgerufenen Funktion leichter zu handhaben sind, als Zeiger.



  • Ich sehe das genauso, Belli. Und auch ich krieg dafür immer wieder eins auf den Deckel... 😉



  • Belli schrieb:

    Die NULL-Zeiger - Geschichte kann man evtl. anders lösen (Überladung), aber selbst wenn nicht, weiß ich, wenn ich das konsequent durchziehe, daß ich nicht von Parameter-Änderungen überrascht werden kann, wenn ich nicht eine Adresse übergebe.

    Ach so, du willst also jedesmal wo ein NULL-Zeiger möglich, und eine Modifizierung erlaubt ist, eine Überladung schreiben? Viel Spaß.

    Aber mit den "selbst wenn nicht" und "konsequent durchziehe" wiedersprichst du dir selbst.

    Weitere Diskussionen lasse ich aber erstmal ruhen, sonst regt sich wieder jemand wegen überlangen Threads auf 😉





  • Chris++ schrieb:

    Also Referenzen sind ja im Pinzip Pointer die nicht NULL sein dürfen.

    Aber können!

    void f (int &i)
    {
      i = 1; /* Krach */
    }
    
    int main()
    {
      f ((int&)*(int*)0);
    }
    


  • Sowas macht man ja auch nicht! 🙂



  • David_pb schrieb:

    Sowas macht man ja auch nicht! 🙂

    Ja, trotzdem kann eine Referenz überall hin zeigen wie ein Zeiger. Cast sei Dank.



  • [quote="asc"]

    Belli schrieb:

    Ach so, du willst also jedesmal wo ein NULL-Zeiger möglich, und eine Modifizierung erlaubt ist, eine Überladung schreiben?

    Nein, wo eine Modifizierung in der Funktion erforderlich ist, nehme ich sowieso Zeiger.
    Eine Überladung ist maximal dort erforderlich, wo ich wegen Nichtmodifikation normalerweise eine konstante Referenz übergeben würde, aber 0(NULL) angeben können muß.



  • c++fan 2009 schrieb:

    David_pb schrieb:

    Sowas macht man ja auch nicht! 🙂

    Ja, trotzdem kann eine Referenz überall hin zeigen wie ein Zeiger. Cast sei Dank.

    Auf fast alles! 😉



  • Belli schrieb:

    ...wenn ich Referenzen immer als const übergebe, und nur in Fällen, wo ich ändern können muß, Zeiger benutze (eventuell noch, wenn ich 0(NULL) übergeben können muß), erleichtert mir das die Lesbarkeit, weil ich mich dann auf bestimmte Dinge verlassen kann....

    Hmmm .... non-const-Referenzen und const-Zeiger verwendest Du nicht?

    Ich verlasse mich inzwischen lieber nur noch auf Dinge, die mir der Standard zusichert - alles Andere beißt Dir sowieso in den Hintern, weil es genau in dem einen von 100 Fällen nicht stimmt, auf den es ankommt.

    Ist das gleiche Argument wie bei Variablenbenamsung wie pZeiger , aucArray und m_Member ... es gibt nichts Schlimmeres als sich auf Unzuverlässiges zu verlassen ... dann ist man verlassen. 😉

    Belli schrieb:

    ...Wenn man nun den Code liest und verstehen will, ist das an der Stelle, wo der Aufruf erfolgt, dann nicht erkennbar, wenn man Referenzen benutzt, weil der Aufruf mit einer Referenz sich nicht von einem Aufruf mit einem Wert unterscheidet....

    Was ich für eine gute Idee halte, weil zum Thema "Änderbarkeit" das const-Konzept eingeführt wurde und "Zeiger/Referenzen" damit eigentlich nichts zu tun haben.

    Gruß,

    Simon2.



  • c++fan 2009 schrieb:

    David_pb schrieb:

    Sowas macht man ja auch nicht! 🙂

    Ja, trotzdem kann eine Referenz überall hin zeigen wie ein Zeiger. Cast sei Dank.

    Nen Cast braucht man ja nicht unbedingt, man dereferenziert einfach den Zeiger.

    int* foo();
    void safe( int& i );
    
    safe( *foo() );
    


  • c++fan 2009 schrieb:

    Chris++ schrieb:

    Also Referenzen sind ja im Pinzip Pointer die nicht NULL sein dürfen.

    Aber können!
    ...

    Aber da "ist" doch die Referenz nicht 0, sondern du de-referenzierst halt einen ungültigen Zeiger:

    c++fan 2009 schrieb:

    f ((int&)     
       *        // hier dereferenzierst Du einfach 0
    (int*)0);
    

    Dass das zu einem undefinierten Verhalten führt, ist weder überraschend noch hat es was mit Referenzen zu tun.

    ich weiß, dass Referenzen auch auf ungültige Objekte zeigen können, aber allein die Tatsache, dass man das bei Referenzen gar nicht überprüfen KANN, erledigt die ursprüngliche Diskussion darüber, ob man es bei Zeigern soll/muss oder nicht.
    Ich darf mich bei der Verwendung von Referenzen darauf verlassen, dass mir der Aufrufer ein gültiges Objekt übergibt, weil ich gar keine Alternative habe....

    c++fan 2009 schrieb:

    David_pb schrieb:

    Sowas macht man ja auch nicht! 🙂

    Ja, trotzdem kann eine Referenz überall hin zeigen wie ein Zeiger. Cast sei Dank.

    Dem würde ich auch widersprechen und sagen: Man darf in C halt rumcasten und dereferenzieren wie man will ... aber das hat nichts mit Referenzen zu tun.
    Genauso könnte ich sagen: "Ein int kann auch ein string sein" ... bloß weil ich das irgendwie miteinander "vercasten" kann.

    Gruß,

    Simon2.



  • Belli schrieb:

    Ich bleibe dabei, wenn ich Referenzen immer als const übergebe, und nur in Fällen, wo ich ändern können muß, Zeiger benutze (eventuell noch, wenn ich 0(NULL) übergeben können muß), erleichtert mir das die Lesbarkeit, weil ich mich dann auf bestimmte Dinge verlassen kann.

    sehr schön. 🙂
    so halte ich das auch.
    aber swap wird mit referenzen gebaut, weil das schon immer so war.



  • volkard schrieb:

    Belli schrieb:

    Ich bleibe dabei, wenn ich Referenzen immer als const übergebe, und nur in Fällen, wo ich ändern können muß, Zeiger benutze (eventuell noch, wenn ich 0(NULL) übergeben können muß), erleichtert mir das die Lesbarkeit, weil ich mich dann auf bestimmte Dinge verlassen kann.

    sehr schön. 🙂
    so halte ich das auch.
    aber swap wird mit referenzen gebaut, weil das schon immer so war.

    Schonmal meinen Code gelesen? ... oder den von Anderen, die diese Regel nicht beherzigen? :p 😉

    Gruß,

    Simon2.

    P.S.: Mir ist nicht nur kein Code untergekommen, der diese Regel umgesetzt hätte ... auch noch keiner, bei dem drüber steht "ACHTUNG! Dieser Code hält sich nicht konsequent an die volkhardsche Parameter-Zeigersemantik!"



  • bei fremden Code kannst du dich nicht drauf verlassen und bei eigenem Code kannst du auch folgendes schreiben:

    #define OUT
    
    void foo(int& i)
    	{
    	i = 3;
    	}
    
    int main()
    	{
    	int a;
    	foo(OUT a);
    	}
    

    Is zwar ungewohnt, aber man sollte wissen was gemeint ist! Dass das nicht üblich ist, ist klar, aber es ist nicht weniger sicher als davon auszugehen, dass Pointer automatisch outputparameter sind?

    (Zur Anmerkung: natürlich denke ich nicht, dass man das machen sollte, aber es ist genau das selbe o.O OUT kann alles sein, ist im Prinzip aber nur zu lesbarkeit, genau so verhält es sich mit Pointern an dieser Stelle. Im Prinzip ist OUT sogar besser, weil man dann intern Referenzen verwenden kann und nicht auf die Gefahr läuft 0 übergeben zu bekommen.)


Anmelden zum Antworten