Zeiger oder Referenz



  • Meine bescheidene Laienmeinung tut hier sicher nichts zur Sache, weswegen ich sie nicht kundtun werde, jedoch möchte ich darauf hinweisen, dass Volkard nicht ganz so "alleine" steht wie, behauptet wurde. Stroustrup sagt in "The C++ Language" das selbe unter Anführung gleicher Gründe (S. 107 deutsch, Ausgabe 4).

    Zum Überprüfen der Zeiger auf 0: Weiter oben angeführte C++-FAQ impliziert ähnliches in anderem Kontext: http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.12

    (Kurz: dass eine Überprüfung auf 0 von Zeiger-Argumenten nicht immer notwendig ist.)



  • Konrad Rudolph schrieb:

    (Kurz: dass eine Überprüfung auf 0 von Zeiger-Argumenten nicht immer notwendig ist.)

    Eine Referenz ist kein Zeiger, daher hinkt der Vergleich (falls beabsichtigt).

    Die Ueberpruefung von Zeigern auf 0 bringt relativ wenig, da der Fall selten vorkommt, dass ein falscher Zeiger auf die Adresse 0 zeigt.

    Ausnahme sind z.B. ungepruefte dynamic_cast<> Aufrufe, sowie andere Aufrufe, die einen 0-Zeiger im Fehlerfall zurueckliefern, der dann ungeprueft als Zeiger verwendet wird. D.h. die Pruefung sollte beim Aufrufer erfolgen und nicht beim Aufgerufenen.

    Einziges gutes Argument fuer Zeiger, das mir grade einfaellt, ist, dass die Anzahl von Konstruktoraufrufen durch die Verwendung von Zeigern reduziert werden kann. Dies hat einen definitiven Performance-Impact.

    Aber man kann sich dann gleich fragen lassen, warum man C++ und nicht C benutzt. Der Programmieroverhead in C++ wird durch die Verwendung von Zeigern stark erhoeht, und das Fehlerrisiko steigt.



  • Power Off schrieb:

    Konrad Rudolph schrieb:

    (Kurz: dass eine Überprüfung auf 0 von Zeiger-Argumenten nicht immer notwendig ist.)

    Eine Referenz ist kein Zeiger, daher hinkt der Vergleich.

    Ich bezog mich auf Volkards Aussage "[...] weil es nämlich gar nicht so ist, daß man bei zeigern jeden übergebenen wert auf 0 prüfen sollte."

    Der Programmieroverhead in C++ wird durch die Verwendung von Zeigern stark erhoeht, und das Fehlerrisiko steigt.

    Nun, es ging hier doch nur um Parameterübergabe per Zeiger/Referenz und hier entsteht wohl kaum ein Programmieroverhead durch Zeigerverwendung (zumindest, wenn man sich um Nullwerte keine Sorgen macht).



  • Konrad Rudolph schrieb:

    Nun, es ging hier doch nur um Parameterübergabe per Zeiger/Referenz und hier entsteht wohl kaum ein Programmieroverhead durch Zeigerverwendung (zumindest, wenn man sich um Nullwerte keine Sorgen macht).

    Ja, das stimmt.

    Ich mein ja nur. Wenn wir's schon von Referenzen vs. Zeiger haben. 😉



  • Power Off schrieb:

    Einziges gutes Argument fuer Zeiger, das mir grade einfaellt, ist, dass die Anzahl von Konstruktoraufrufen durch die Verwendung von Zeigern reduziert werden kann. Dies hat einen definitiven Performance-Impact.

    versteh ich nich
    im zusammenhang mit referenzen die als argument einer funktion übergeben werden?
    wo finden da konstruktorenaufrufe statt?



  • Sovok schrieb:

    Power Off schrieb:

    Einziges gutes Argument fuer Zeiger, das mir grade einfaellt, ist, dass die Anzahl von Konstruktoraufrufen durch die Verwendung von Zeigern reduziert werden kann. Dies hat einen definitiven Performance-Impact.

    versteh ich nich
    im zusammenhang mit referenzen die als argument einer funktion übergeben werden?
    wo finden da konstruktorenaufrufe statt?

    Z.B. bei dieser Sequenz:

    #include <string>
    
    using std::string;
    
    void func( const string& str ) {
       // ...
    }
    
    void func2( void ) {
       func( "alpha" );  // versteckter Konstruktor-Aufruf: es wird ein temporaeres string-Objekt angelegt
                         // und danach wieder vernichtet.
       string c = "alpha" + string("beta");  // 2 Konstruktor-Aufrufe, 1 Destruktor-Aufruf, 
                                             // 1 operator+() Aufruf
    }
    


  • Power Off schrieb:

    string c = "alpha" + string("beta");  // 3 Konstruktor-Aufrufe, 2 Destruktor-Aufrufe, 
                                             // 1 operator+() Aufruf
    }
    

    ich zähle aber nur 2 ctor-aufrufe, 1 dtor-aufruf und 1 op+()-aufruf.

    /**
       *  @brief  Concatenate C string and string.
       *  @param lhs  First string.
       *  @param rhs  Last string.
       *  @return  New string with value of @a lhs followed by @a rhs.
       */
      template<typename _CharT, typename _Traits, typename _Alloc>
        basic_string<_CharT,_Traits,_Alloc>
        operator+(const _CharT* __lhs,
    	      const basic_string<_CharT,_Traits,_Alloc>& __rhs);
    


  • volkard schrieb:

    ich zähle aber nur 2 ctor-aufrufe, 1 dtor-aufruf und 1 op+()-aufruf.

    Stimmt, hast recht. Werd's gleich korrigieren! 🙂



  • vorteil von zeigern?

    void func( const string* str ) {
       // ...
    }
    
    void func2( void ) {
    
       func( &string ( "alpha" ) );
    
       //wohl eher 
       string c = string ( "alpha" ) + string("beta");  
       //weil "alpha" sonst nur n zeiger is zu dem du addierst
       //beim zweiten kann ich den vorteil von zeigern auch nich nachvollziehn
       //geht schliesslich garned mit zeigern
    }
    

    [/quote]



  • Gast4 schrieb:

    volkard hat sowieso Unrecht.

    Falsch geraten, Volkard hat immer Recht (naja, fast). Er wird schliesslich nicht umsonst "The Godfather" genannt. 😉

    Trotzdem hab ich seine Ansichten bzgl. Referenzen vs. Zeiger in diesem Punkt nie nachvollziehen können. Mir ist es ehrlich gesagt zu unintuitiv, anhand semantischer Feinheiten zu erlesen, was ein Programm macht. Mir ist zB vollkommen egal, ob da

    add_something(foo)
    

    oder

    add_something(&foo)
    

    steht. Das einzige was mich interessiert, ist, dass something zu foo addiert wird.



  • Mein Senf muss auch noch dazu.
    Ich mache es oft so, dass ich durch die Wahl anzeige was in der Methode passiert.
    Nimmt die Methode einen Pointer, dann kann man sich sicher sein, dass die Methode dem Paramerter verändert.
    Nimmt die Methode eine (const) Referenz, dann ist man sich sicher, dass die Methode den Parameter nicht verändern kann.

    Wenn ich programmiere, dann weiß ich ja was die Methoden die ich aufrufe machen.
    Wenn ich mir später meinen Code wieder durchsehe und Methodenaufrufe mit '&' sehe, dann weiß ich sofort.. oha.. der will einen Pointer -- gut da passen wir mal auf.



  • Also ich kann volkards Meinung in dem Punkt jetzt auch nicht ganz nachvollziehen. Vielleicht ist das aber auch Teil einer Studie und er will nur schauen wieviele Leute in den nächsten Wochen einfach seine Meinung weiterplappern. Vielleicht kommt er aber auch nachher mit dem Bombenargument und begründet seine Meinung zu Pointern.
    Ob die Funktion etwas mit dem übergebenen Objekt macht erkenne ich daran ob die übergebene Referenz const ist oder nicht und Pointer benutze ich nur wenn es mit Referenzen nicht geht bzw. umständlicher wäre. Pointer auf 0 prüfen macht meiner Meinung nach nur dann Sinn wenn das Objekt auch 0 sein kann, also zusätzlich zur Zeigerfunktion einen Status darstellt (diverse Libs machen da regen Gebrauch von).



  • (naja, fast)

    genau das meinte ich 😉



  • hehejo schrieb:

    Nimmt die Methode einen Pointer, dann kann man sich sicher sein, dass die Methode dem Paramerter verändert.
    Nimmt die Methode eine (const) Referenz, dann ist man sich sicher, dass die Methode den Parameter nicht verändern kann.

    Warum macht man das nicht gleich nur mit Referenzen?

    void func( const alpha& a );  // a kann nicht geaendert werden
    void func( alpha& a );        // a kann geaendert werden
    


  • Sovok schrieb:

    string c = string ( "alpha" ) + string("beta");  
    //weil "alpha" sonst nur n zeiger is zu dem du addierst
    

    Das macht nix, weil es einen operator+( const char*, const string& ) gibt, den Volkard ja aufgelistet hat.
    Also ist:

    string c = "alpha" + string("beta");
    

    equivalent zu:

    string c( operator+( "alpha", string( "beta" ) ) );
    


  • Sovok schrieb:

    //wohl eher 
       string c = string ( "alpha" ) + string("beta");  
       //weil "alpha" sonst nur n zeiger is zu dem du addierst
    }
    

    Nein, siehe Volkards letzten Kommentar und seinen Auszug aus den Standard-Headerdateien.



  • groovemaster schrieb:

    Trotzdem hab ich seine Ansichten bzgl. Referenzen vs. Zeiger in diesem Punkt nie nachvollziehen können. Mir ist es ehrlich gesagt zu unintuitiv, anhand semantischer Feinheiten zu erlesen, was ein Programm macht. Mir ist zB vollkommen egal, ob da

    add_something(foo)
    

    oder

    add_something(&foo)
    

    steht. Das einzige was mich interessiert, ist, dass something zu foo addiert wird.

    sematische feinheiten? jup, die würden mich ankotzen. ich will klaren uen einfachen code.
    deswegen will ich keine semantischen feinheiten ausdeuteln müssen, sondern *sofort* sehen, was los ist. insbesondere will ich *nicht* überlegen müssen, was die funktion denn bewerkstelligt. ich kann nicht immer aus dem namen genau folgern, was die funktion macht. vor allem, wenn fremde leute die funktionsnamen erfunden haben.
    und das geht so:

    do_something(foo);//call by value, foo belib unverändert
    

    oder

    do_something(&foo);//foo wird verändert, weil zeiger
    

    und sonst nix.
    also beim lesen sehe ich immer ganz genau, ob ich das foo nur zeige oder ob ich's zum verändern hergebe. ach, ist das eine freude, wenn man mal ein kaputtes foo hat, und sich überlegen muß, welche funktiopn foo kaputtet hat. kann ja nur eine mit &foo ewesen sein.
    außerdem bin ich auch beim programmieren ein wenig feige. sollte das nicht jeder sein? wenn ich meinen funktionen immer nur foo zu fresen gebe, hab ich echt ein besseres gefühl. die paar mal, wo es dann doch &foo sein muß, mach ich das halt, aber immer bewußt. und da weiß ich, daß keiner an meinen daten rumkritzelt, ohne daß ich das bewußt zugelassen habe.
    der stil ist also sowas wie const, nur eben als forderung von der callerseite her und nicht als versprechen von der calleeseite her und ergänzt die strenge typprüfung von c++ dahingehend, daß man schon wieder einen haufen aus-versehen-fehler zu compilerfehlern gemacht hat.



  • Referenzen sind, grob gesagt, die Zeiger fuer Leute die Zeiger nicht raffen...
    Man beschneidet seine Programme durch konsequente Nutzung von Referenzen selbst.
    Wie prueft man denn die Gueltigkeit einer Referenz? Bei Zeigern geht das ganz einfach. Bei Referenzen ist dies unmoeglich. Zeiger koennen euren Programmen bei geschickter Nutzung zu brutaler Geschwindigkeit verhelfen. Klar, fuer operatoren-ueberladung, catchen, copy c'tor & co. sind Referenzen noetig.
    Ich nutze Referenzen quasi nie ausser bei letzterem.
    Aber ausserdem gibt es keine Referenz-Arithmetik... 😉

    👍 @Volkard

    👍 Zeiger



  • Walli schrieb:

    Ob die Funktion etwas mit dem übergebenen Objekt macht erkenne ich daran ob die übergebene Referenz const ist oder nicht

    das ist aber nur die eine richtung. die funktion verspricht dir, daß da nix geändert wird. bei nur fehlerfreien funktionen, die du benutzt, mag das ok sein. wenn ich schreibe

    foo(v);
    

    dann brauche ich nicht nachzuschauen, ob foo das v verändert oder nicht. ok, namen wie foo verwenet man nicht. ich brauche aber auch nicht anhand des namens zu überlegen, und das klappt eh nicht immer.

    f.add(v);
    

    soll jetzt v zu f addiert werden oder f zu v? uneindeutige namen wir's immer geben. übrigens wird hier v genommen und zu f addiert, was ja klar ist, weil v nicht verändert wird.



  • volkard schrieb:

    do_something(foo);//call by value, foo belib unverändert
    do_something(&foo);//foo wird verändert, weil zeiger
    

    Es ist genau umgekehrt:

    void do_something( const string& foo );  // Call-By-Reference Parameter  ("foo" ist eine Referenz)
    void do_something( string* foo );        // Call-By-Value Parameter      ("foo" ist ein Pointer und damit ein Value!)
    

    also nochmal:

    do_something(foo);   // call by reference
    do_something(&foo);  // call by (address) value
    

    😃


Anmelden zum Antworten