Rückgabewert: Referenz



  • Hey Leute,
    hätte da mal eine Frage. Leider will es nicht in meinem Kopf, wieso man den Rückgabewert einer Funktion oder Memberfunktion(Klasse) per Referenz geben sollte.

    Beispiel: public: int& test();

    Ich versuche mal zu erklären wie ich das verstanden habe:
    Also test gibt eine Referenz zurück von einem Objekt was nicht lokal ist, sonst würde es ja nicht mehr existieren, damit man damit weiterarbeiten kann.
    Ist das erstmal so richtig? Könnte mir bitte einer eventuell das genauer erklären. Im Internet findet man natürlich viel darüber, aber ich checks nicht recht.

    Vielen Dank für die Hilfe.
    MFG
    Aknayirp



  • Aknayirp schrieb:

    Hey Leute,
    hätte da mal eine Frage. Leider will es nicht in meinem Kopf, wieso man den Rückgabewert einer Funktion oder Memberfunktion(Klasse) per Referenz geben sollte.

    Beispiel: public: int& test();

    Ich versuche mal zu erklären wie ich das verstanden habe:
    Also test gibt eine Referenz zurück von einem Objekt was nicht lokal ist, sonst würde es ja nicht mehr existieren, damit man damit weiterarbeiten kann.
    Ist das erstmal so richtig? Könnte mir bitte einer eventuell das genauer erklären. Im Internet findet man natürlich viel darüber, aber ich checks nicht recht.

    Vielen Dank für die Hilfe.
    MFG
    Aknayirp

    Referenzen sind an sich total unnütiger Quatsch. Völlig sinnlos. Java-Verrücktheit. Schlicht Blödsinn.

    Aaaber, sie sind notwendig, om Operatoren wie ++ oder [] für eigene Klassen definieren zu können, damit sie die eigenen Klassen anfühlen wie eingebaute Typen. Und das Gleich-Anfühlen ist wirklicg gut, hat sich unglaublich bewährt.

    Meide Referenzen, aber nimm sie, wenn sie sich aufdrängen.

    Deine Implemetierung eines Vector mag einfach

    double& operator[](size_t index){
       assert(index<size);
       return data[index];
    }
    

    oder so. Man kann damit manchmal fein arbeiten. Selten. Also im Usercode nie eigentlich. Aber für Bibliotheken isses manchmal echt genau gut.



  • Aknayirp schrieb:

    Beispiel: public: int& test();

    Ich versuche mal zu erklären wie ich das verstanden habe:
    Also test gibt eine Referenz zurück von einem Objekt was nicht lokal ist, sonst würde es ja nicht mehr existieren, damit man damit weiterarbeiten kann.
    Ist das erstmal so richtig?

    Ja.

    Könnte mir bitte einer eventuell das genauer erklären. Im Internet findet man natürlich viel darüber, aber ich checks nicht recht.

    Vom "was kann ich damit machen", ist eine Referenz zurückzugeben das selbe wie einen Zeiger zurückzugeben.
    (Vom "wie macht man es"/"wie schreibt man es hin" ist es ganz anders, aber darum geht's erstmal nicht.)
    D.h. du kannst über die Referenz dann auf das Original-Objekt zugreifen, statt auf eine Kopie davon.

    Das kann, wie volkard anhand des operator [] Beispiels gezeigt hat, sehr nützlich sein. Ich will ja schreiben können myVector[index] = 123; . Natürlich könnte man auch myVector.SetElement(index, 123); schreiben, aber das wäre dann nicht sehr "C++-ig".
    U.a. weil C++ Templates hat, und da ist es sehr praktisch wenn man Klassen wie std::vector gleich wie ein Array behandeln kann.

    Weiters kann man Referenzen oft dort als Returntyp verwenden wo man typischerweise sonst einen Zeiger als Returntyp genommen hätte. z.B. wenn Zeigerarithmetik auf dem Returnwert keinen Sinn macht und der Zeiger niemals NULL sein kann/darf.

    Das heisst natürlich nicht dass man muss.

    Ich persönlich bin eher der Meinung: wenn's keinen guten Grund dafür gibt, dann Finger weg.

    Für private Hilfsmethoden die bloss eine Referenz auf ein Member bzw. ein in einem Member gespeichertes Element zurückgeben: OK. Das kann oft praktisch sein. Ansonsten lieber kopieren oder ggf. Zeiger (evtl. auch nen shared_ptr ) zurückgeben.



  • volkard schrieb:

    Referenzen sind an sich total unnütiger Quatsch. Völlig sinnlos. Java-Verrücktheit. Schlicht Blödsinn.

    Andersrum: Einen potenziellen Nullpointer zu dereferenzieren ist gefährlich. Völlig sinnlos. Java-Verrücktheit. Schlicht Blödsinn.

    Es ist Aufgabe des Typsystems, so einem Pointermissbrauch vorzubeugen. Die Lösung? C++-Referenzen.



  • template <class T> T &pointerToReference(T *t){
    	return *t;
    }
    template <class T> const T &pointerToReference(const T *t){
    	return *t;
    }
    

    Womit Referenzen kein bisschen besser sind als Pointer. Obwohl man natürlich argumentieren könnte, dass die Funktion irgendwie logisch falsch ist.

    Ich persönlich mag Referenzen. Wenn man zum Beispiel einen Dateipfad übergeben muss macht sich const std::string & ganz gut, funktioniert mit strings, mit char * und mit Stringliteralen.
    Und damit ich mir nicht immer merken muss, ob ich jetzt foo.bla oder foo->bla schreiben muss benutze ich in letzter Zeit immer Referenzen. Ist aber wahrscheinlich nur eine Laune.



  • Danke erstmal für die ganzen Antworten. Da wir das in der Uni benutzen, muss ich das halt drauf haben.

    Hier mal ein TestCode bei dem ich versuche es anzuwenden(auch wenn es hier unnötig ist):

    #include <iostream>
    
    using namespace std;
    
    class test
    {
    private: int a;
    public: test(){a=6;}
            int& print(){return a;}
    };
    
    int main()
    {
      test one;
      int& b = one.print();
      b = 8;
      cout << one.print() << endl;
    
      cin.clear();
      cin.ignore(cin.rdbuf() ->in_avail());
      cin.get();
    
      return 0;
    }
    

    Eigentlich sollte doch, weil b=8 gesetzt wurde a doch jetzt auch 8 sein, weil b doch eine referenz auf a ist richtig? Passiert so aber nicht, wahrscheinlich weil a private ist aber ist der Gedanke ansonsten richtig so?(abgesehen natürlich davon, dass a so nicht verändert werden kann da es private ist)



  • nwp3 schrieb:

    Womit Referenzen kein bisschen besser sind als Pointer. Obwohl man natürlich argumentieren könnte, dass die Funktion irgendwie logisch falsch ist.

    Pointer und Referenzen sind keine Bijektion. Spätestens bei *t hast du UB.

    Also musst du schreiben

    template <class T> T &pointerToReference(T *t){
        assert(t); // verhindert zumindest den offensichtlichsten Missbrauch
        return *t;
    }
    

    und schon hast du meine Definition der Referenz.

    nwp3 schrieb:

    Und damit ich mir nicht immer merken muss, ob ich jetzt foo.bla oder foo->bla schreiben muss benutze ich in letzter Zeit immer Referenzen. Ist aber wahrscheinlich nur eine Laune.

    Mach ich persönlich auch gern und führt dazu, dass ich unbedingt den Punkt-Operator überladen möchte. Naja, nichts was sich nicht durch eine zusätzliche Indirektion lösen liesse.



  • nwp3 schrieb:

    Ich persönlich mag Referenzen.

    Natürlich sind Referenzen sinnvoll. Abgesehen von der Semantik machen sie auch in vielen Situationen Code lesbarer. Manchmal ist volkard eben gerne ein Dokmatiker 😉



  • hahaha also diesen Austausch habe ich hier nicht erwartet. Ich bin nun ganz durcheinander. Ich programmiere halt nicht solange und immer wenn ich so eine Funktion sehe mit einer Referenz als Rückgabewert, bin ich mir nicht ganz sicher was genau getan wird. Wird wohl an der wenigen Erfahrung liegen.
    Wäre schön wenn ihr euch nochmal oben meinen Code anschauen könntet, damit ich Bescheid weiß, ob ich völlig daneben liege oder auf dem richtigen Weg bin.



  • ...



  • Sehr schön bisher. 👍
    Bitte bleibt im Folgenden noch ein wenig auf dem Teppich.



  • volkard schrieb:

    Sehr schön bisher. 👍

    Irgendwie bin ich grad nur blöd... Meinst du jetzt damit, dass ich richtig liege mit dem Code?



  • Aknayirp schrieb:

    Danke erstmal für die ganzen Antworten. Da wir das in der Uni benutzen, muss ich das halt drauf haben.

    Hier mal ein TestCode bei dem ich versuche es anzuwenden(auch wenn es hier unnötig ist):

    #include <iostream>
    
    using namespace std;
    
    class test
    {
    private: int a;
    public: test(){a=6;}
            int& print(){return a;}
    };
    
    int main()
    {
      test one;
      int& b = one.print();
      b = 8;
      cout << one.print() << endl;
      
      cin.clear();
      cin.ignore(cin.rdbuf() ->in_avail());
      cin.get();
    
      return 0;
    }
    

    Eigentlich sollte doch, weil b=8 gesetzt wurde a doch jetzt auch 8 sein, weil b doch eine referenz auf a ist richtig? Passiert so aber nicht

    Es sollte nicht nur 8 sein, es ist auch 8. Was für einen Compiler verwendest du um Himmels Willen?

    http://ideone.com/GNtJeo

    BTW: verwende [cpp] Tags statt [code], dann macht das Forum C++ Syntax-Highlighting.

    , wahrscheinlich weil a private ist aber ist der Gedanke ansonsten richtig so?(abgesehen natürlich davon, dass a so nicht verändert werden kann da es private ist)

    Nope, ganz falsch. public vs. private spielt hier überhaupt keine Rolle. Wenn deine Klasse eine Referenz auf irgendwas hergibt was private ist, dann ist sie selbst schuld. Der Compiler greift da nicht ein und schützt das Ding dann automagisch vor Veränderung, oder sorgt für eine Kopie wo vom Sprachstandard her keine erlaubt ist.



  • Also ich benutze Visual C++ 2010 Express. Keine Ahnung wieso der dann streikt. Also ich erhalte keine Fehlermeldung, sondern in der Konsole erscheint nix. hmm
    vllcht sollte ich mal den Laptop neustarten...

    P.S: Und danke für den Tipp mit cpp; werde es beim nächsten mal beachten:D



  • ...



  • Aknayirp schrieb:

    Also ich benutze Visual C++ 2010 Express. Keine Ahnung wieso der dann streikt. Also ich erhalte keine Fehlermeldung, sondern in der Konsole erscheint nix.

    Habs eben im 2010er ausprobiert. Es funktioniert wie gewünscht.



  • Aknayirp schrieb:

    sondern in der Konsole erscheint nix.

    irgendwas muss da erscheinen. Mach mal Ausgaben vorher und hinterher

    cout << "Value: '" << one.get_a() << "'\n";
    

    oder meinst du, das sich die Konsole sofort wieder schließt?



  • Ne, da wurde irgendwie nichts angezeigt. Aber jetzt funktioniert es so wie es sollte. Vielen Dank bis hierher!

    Hab jetzt erneut eine Frage, die auch mit Referenzen zutun hat, deshalb schreib ich mal hier weiter und mache keinen neuen Thread auf; es geht um diese Template-Klasse:

    #include <iostream>
    
    template <class T,unsigned int N> class CArray{
      private:
        T* entries;
      public:
        T& operator[](unsigned int index);
        void setField();
        CArray(const CArray&);
        CArray();
        ~CArray();
    };
    
    template <class T, unsigned int N> T& CArray<T,N>::operator[](unsigned int index)
    {
      static T& test = *(entries+index);
    
      return test;
    }
    template <class T,unsigned int N> void CArray<T,N>::setField()
    {
      for(int i=0; i<N; i++)
        *(entries+i) = 2;
    }
    
    template <class T, unsigned int N> CArray<T,N>::CArray()
    {
      entries = new T[N];
    }
    
    template <class T, unsigned int N> CArray<T,N>::~CArray()
    {
      delete entries;
    }
    

    Hab jetzt mal alles kopiert, aber es geht um die OperatorFunc. Das es eine Referenz zurückgeben soll, ist mir vorgegeben. Aber da durch diese Funktion ich Zugriff auf das Array haben will, muss ich ja entries zurückgeben, aber das ist ja ein pointer und keine Referenz.
    Da hab ich einfach eine static Variable genommen, damit diese auch nach Verlassen der Funktion vorhanden ist und ich so Zugriff haben kann.
    Das Programm funktioniert auf jeden Fall so, aber geht das auch anders? Ich Frage, weil ich in der nächsten Teilaufgabe einen Kopierkonstruktor erstellen soll. Aber das wäre doch dann nicht gut im Zusammenhang mit der static Variable, da diese ja nur einmal da ist für alle.
    Ich hoffe ihr versteht, was ich meine.

    MFG
    Aknayirp



  • Returne einfach *(entries + index).
    entries bleibt ja da, solange die Klasse exisitiert.
    Und entries enthält ja die ganze Zeit N Elemente.
    Also bleibt auch das Element entries + index erhalten, solange der index < N ist.

    BTW: Anstatt *(entries + index) kann man auch schreiben: entries[index].

    BTW 2: Warum dynamischer Speicher? N ist doch schon zur Compilezeit bekannt, also kann man auch einfach im Rumpf der Klasse schreiben:
    T entries[N].



  • Nathan schrieb:

    Returne einfach *(entries + index).
    entries bleibt ja da, solange die Klasse exisitiert.
    Und entries enthält ja die ganze Zeit N Elemente.
    Also bleibt auch das Element entries + index erhalten, solange der index < N ist.

    BTW: Anstatt *(entries + index) kann man auch schreiben: entries[index].

    BTW 2: Warum dynamischer Speicher? N ist doch schon zur Compilezeit bekannt, also kann man auch einfach im Rumpf der Klasse schreiben:
    T entries[N].

    Das ist richtig, nur ich dachte ich muss eine Referenz zurückgeben, aber entries ist doch ein Pointer. Oder geht das trotzdem?
    BTW = jau, das wußte ich:D
    BTW 2 = Weil in der Aufgabe steht, das der Speicher vom Heap kommen soll.

    EDIT: Habe es grad das ausprobiert und es funktioniert, aber gestern ging es nicht komisch...
    Wäre gut, wenn mir nochmal jmd. erklären könnte, wieso ich den Pointer zurückgeben kann, obwohl die Funktion eine Referenz zurückgeben soll.
    Vielen Dank


Anmelden zum Antworten