Referenzen und Zeiger



  • eine noch schönere Variante ist folgendes:

    NEVER DO THIS! NIEMALS MACHEN!

    std::string& genMessage(uint32 i)
    {
      static std::string abc;
      /* mach was mit dem Input und beschreibe den String */
    
      return abc;
    }
    

    NEVER DO THIS! NIEMALS MACHEN!

    Das hat ganz böse nebenwirkungen bei multithreading.

    Sowas sieht man hin und wieder bei Anfängern, die gerade erfahren haben, dass bei Rückgabewerten temporäre Objekte erstellt werden, die sie so eingespart werden sollen.



  • freakC++ schrieb:

    wo liegt dann der Bezug auf einer anderen Variable?

    Muss nicht, dass nennt man call-by-value.



  • eine referenz kann man sich am besten als selbstreferenzierenden zeiger
    vorstellen.

    gehen wir von deinem beispielscode aus:

    string& message()
    {
     static string str = "Hallo";
     return str;
    }
    
    int main()
    {
     message() = "Tschüss";
     string s=message();
     cout << s << endl;
    }
    

    jetzt stell dir vor, die referenzen wären zeiger (zeig mir einen realen
    compiler, der das so nicht handhabt), und bei jedem zugriff bekämen wir
    unser objekt (per * dereferenziert):

    string *message() // die referenz in form einer addresse
    {
     static string str = "Hallo";
     return &str; // referenz, also addresse zurückgeben
    }
    
    int main()
    {
     *message() = "Tschüss"; // addresse holen, dereferenzieren und reinschreiben
     string s = *message();
     cout << s << endl;
    }
    

    die addresse einer referenz bekommst du nicht, da sie für dein objekt steht.
    kann man sich auch herleiten:

    int i;
    int &r = i;
    
    int *ptr = &r;
    
    // wird zu
    
    int i;
    int *r = &i;
    
    int *ptr = &*r; // das kürzt sich!!
    

    ich wette mit dir, dass beide versionen auf assemblerebene gleich aussehen.
    die größen unterschiede liegen in der syntax. so brauchst du eine referenz
    nicht zu dereferenzieren (auch das kann man aus dem zeigerbeispiel erkennen).

    auf null setzen geht auch nicht.
    (mit einem trick schon: int &r = *(int *)0; jeder zugriff würde aber
    abschmieren).

    so.

    das ganze war eine veranschaulichung zur besseren verständniss. nichts davon
    ist wirklich durch den c++ standard vorgeschrieben.

    (offtopic: ist die zahl der bits in einem byte per standard festgelegt??)

    in den meisten fällen kannst du einen zeiger durch eine referenz ersetzen,
    andersrum (referenz durch zeiger) geht das immer (außer bei diesem const-teil...)

    wenn du z.b. deine referenzen/zeiger oft umsetzt oder als ungültig markierst (0),
    ist ein zeiger die bessere wahl. willst du ein objekt wie ein anderes
    behandeln (proxy-klassen) oder die schöne streaming syntax nutzen (cout <<),
    sind referenzen besser.
    wenn du einen konstanten parameter hast, der nicht kopiert werden soll, überzeugt
    eine referenz schon aus syntaktischen gründen.

    dynamische speicherverwaltung (new/delete) ist aber wieder ein klarer fall für
    zeiger. kein mensch schreibt sowas:

    obj &o = *new obj;
    delete &o;
    


  • Hey,
    vielen Dank für die ausführlichen Erklärungen. Ich denke, dass ich es verstanden habe. Falls dennoch Fragen wieder aufkommen sollte, dann werde ich mich melden!

    Bis dann
    lg, freakC++



  • Hallo zusammen,
    da bin ich nochmals. Ich habe nochmal eine Frage zu diesem Aufruf:

    int& max(int x, int y)
    {
     return x > y ? x : y;
    }
    

    Warum wird hier der Compiler eine Fehlermeldung ausgeben. Kann man überhaupt noch einen Rückgabetyp einer Funktion vom Typ "Referenz auf Typ" machen? Könnt ihr mir das nochmals an einem guten Beispiel erläutern?

    Vielen Dank
    lg, freakC++



  • freakC++ schrieb:

    int& max(int x, int y)
    {
     return x > y ? x : y;
    }
    

    Das klappt nicht, weil x eine lokale Variable innerhalb der Funktion ist und totgeht, wenn die Funktion verlassen wird. Eine referenz auf was totes ist nicht gut.

    int& max(int& x, int& y)
    {
     return x > y ? x : y;
    }
    

    So wäre erzwungen, daß der Aufrufer Referenzen auf außen lebende Variablen reinsteckt und eine Referenz auf eine dieser außen lebenden Variablen zurückzugeben, istr ok.



  • Hallo,
    das leuchtet mir zwar ein, doch dann verstehe ich dieses Beispiel nicht 😃 :

    string& message() 
    { 
     static string str = "Hallo"; 
     return str; 
    }
    
    int main()
    {
    	string str = message();
    	cout << str << endl;
    	return 0;
    }
    

    Dieses Beispiel hatten wir zwar schonmal, aber egal. Wenn ich nun das static wegmache, wird str auch zu einer lokalen Variable. Wenn man mal vom Logischen absieht: Das Programm wird auch ohne static kompiliert. Warum kompiliert dann das andere Beispiel nicht. Da sind doch auch "nur" lokale Variablen?!

    Vielen Dank
    lg, freakC++



  • Grundsätzlich kompiliert auch das obige Beispiel. Benenn es einfach mal um. 😉

    Das liegt daran, dass max ein Makro ist und dann bekommt der Compiler da ein sehr merkwürdiges Konstrukt vorgelegt. 😉


  • Mod

    freakC++ schrieb:

    Hallo zusammen,
    da bin ich nochmals. Ich habe nochmal eine Frage zu diesem Aufruf:

    int& max(int x, int y)
    {
     return x > y ? x : y;
    }
    

    Warum wird hier der Compiler eine Fehlermeldung ausgeben.

    Warum sollte er?



  • camper schrieb:

    freakC++ schrieb:

    Hallo zusammen,
    da bin ich nochmals. Ich habe nochmal eine Frage zu diesem Aufruf:

    int& max(int x, int y)
    {
     return x > y ? x : y;
    }
    

    Warum wird hier der Compiler eine Fehlermeldung ausgeben.

    Warum sollte er?

    neben dem von drakon vermuteten fakt, dass es ein max-makro gibt, gibts auch noch die möglichkeit, dass er algorithm included hat und using namespace std geschrieben hat - dann wird vermutlich kommen, dass man fkt. nicht mittels rückgabe-typ überladen kann...

    bb


  • Mod

    unskilled schrieb:

    neben dem von drakon vermuteten fakt, dass es ein max-makro gibt

    achso, dafür gibts NOMINMAX.

    unskilled schrieb:

    gibts auch noch die möglichkeit, dass er algorithm included hat und using namespace std geschrieben hat - dann wird vermutlich kommen, dass man fkt. nicht mittels rückgabe-typ überladen kann...

    Stimmt, allerdings stehen in std nur zwei Funktionstemplates für max, kein Problem also.



  • camper schrieb:

    unskilled schrieb:

    neben dem von drakon vermuteten fakt, dass es ein max-makro gibt

    achso, dafür gibts NOMINMAX.

    im nachhinen gibts dafür nur noch #undef min bzw #undef max, da jeder, der die windows-header included, NOMINMAX davor definieren müsste - machen aber die wenigsten (libs)... würd ich mich auch nie drauf verlassen...

    camper schrieb:

    unskilled schrieb:

    gibts auch noch die möglichkeit, dass er algorithm included hat und using namespace std geschrieben hat - dann wird vermutlich kommen, dass man fkt. nicht mittels rückgabe-typ überladen kann...

    Stimmt, allerdings stehen in std nur zwei Funktionstemplates für max, kein Problem also.

    stimmt ^^

    bb



  • Hallo zusammen,
    jetzt hab' ich es begriffen. Der Code kompiliert nun auch.

    Bis bald
    lg, freakC++


Anmelden zum Antworten