1

C++: Wann Refernzübergabe und wann Zeigerübergabe in Funktion?

Frage von Monko Monko

Wann nimmt man einen Parameter per Referenz, wann per Pointer/Zeiger?

void TestReference(int &a);
void TestPointer  (int *a);

Mir ist schon klar, dass man bei Zeigern -> bzw. *(a). benutzen muss und Referenzen ganz normal benutzt werden können.

Doch wann welche Methode?

Fragen zu gleichen Themen finden

Antworten (4)

  • 4
    Hilfreichste Antwort ausgezeichnet vom Fragesteller
    Antwort von WhiteGandalf WhiteGandalf

    Beides sind Varianten, die identische HANDLUNGEN ausdrücken und damit zu identischem Maschinencode und identischem Verhalten führen, aber syntaktisch dem Programmierer gegenüber unterschiedlich erscheinen.

    Kurzdarstellung: Eine Referenz ist ein IMPLIZIT DEREFERENZIERTER Pointer. (Der Compiler erledigt das zur Compilezeit, gelle? Mit dem Verhalten zur Laufzeit hat das mitnichten was zu tun!)

    Das führt dazu, daß der Programmierer bei einer Referenz KEINE DEREFERENZIERUNG im Quelltext notieren braucht (und das auch nicht darf, gelle?), um an denn Variablenwert zu kommen.

    Das wiederum führt aber im Gegenzug auch dazu, daß der Programmierer bei einer Referenz im Nachhinein nicht mehr den Zeiger verändern kann.

    Dieses Verhalten kann man sich AUCH als implizites Suchen/Ersetzen vorstellen: Immer, wenn man eine Referenz hinschreibt, ist dies dasselbe, als hätte man einen Pointer hingeschrieben UND eine Dereferenzierung an diesen dran. Bzw. überall, wo man eine Pointer-Dereferenzierung notiert, kann man das auch gleich als Referenz schreiben.

    Fazit: Mit Referenzen hat man es als Programmierer syntaktisch LEICHTER, Pointer zu verwenden, und zwar GENAU DANN, wenn diese Pointer nicht die Stelle ändern brauchen, wo sie hin zeigen.

    Kommentar von DarkIllusion DarkIllusion

    Danke, das ist interessant.

    Kommentar von DarkIllusion DarkIllusion

    Also völlig richtig, der Funktionsaufruf Call-by-Pointer war mir nicht bekannt und hab mich noch mal belesen. Tatsächlich scheint es den Aufruf Call-by-Reference nicht bei allen Programmiersprachen zu geben, allerdings in C++, was einiges erleichter, wie von WhiteGandalf beschrieben.

    Kommentar von Monko Monko

    Tolles Fazit!

    Kannst du mir mal kurz ein Beispiel sagen, in dem die Pointer nicht die Stelle ändern wo sie hinzeigen?

    Mir fällt jetzt immer nur eine Liste ein, aber das geht ja nicht :D

    Kommentar von CaroAs CaroAsCaroAs

    Stelle Dir eine zweidimensionale Struktur vor, die als eindimensionales Array abgelegt wird. Nun brauchst Du einen Laufpointer, der in einer Schleife durch das Array wandert und alle Adressen erreicht! Das kannst Du nicht mit Referenzen erreichen, da diese konstante Pointer sind.


    Ein zweites Beispiel wäre, wenn Du den Typ des Wertes nicht kennst und der Typ anhand weiterer Informationen festgestellt werden kann. Dann kannst Du mit void * ptr den Typ freihalten und später nach dem entsprechenden Typ casten (so ist quasi die ganze Windows-API aufgebaut)

    Kommentar von WhiteGandalf WhiteGandalfWhiteGandalf
    1. Shortcut für verschachtelte Daten:

      int& zaehler = block.inhalt[3].absatz[4].vorderteil.summe; zaehler++;

    2. Shortcut für Anpassungen von kopierten Programmabschnitten:

    vorher: void f(int * p; int n) { for(int i = 0; i < n; i++) cout << p[i] << ", "; vieletausendzeilennochund_nöcher(p, n); }

    nachher: void f(vector v) { int& n = v.size; int*& p = v.ptr;

        // und ohne ein einziges Bit Änderung...
        for(int i = 0; i < n; i++) cout << p[i] << ", ";
        viele_tausend_zeilen_noch_und_nöcher(p, n);
    }
    
    1. (Nein: Drittens!) Funktionsparameter, um (a) redundantes Kopieren zu vermeiden und (b) mehrere unabhängige Werte returnen zu können...

      String nextword(Text & komplexeDaten, int& position) { while(isWhitespace(komplexeDaten.getChar(position))) position++; int start = position; while(!isWhitespace(komplexeDaten.getChar(position))) position++; int stop = position;

      return komplexe_Daten.subString(start,stop);
      

      }

    Es gibt garantiert noch weitere Anwendungen...

    (Das die Forums-Software hier die Formatierung dermaßen vergewaltigt, dafür kann ich nichts, gelle?)

    Kommentar von Monko Monko

    Ja die Codeformatierung ist nicht so dolle hier ;)

    Vielen an alle! 1 Frage noch: Sollte man Referenzen auch als Membervariablen innerhalb einer Klasse nutzen oder dort eher Pointer?

    Kommentar von WhiteGandalf WhiteGandalfWhiteGandalf

    Auch die Member-Variablen in Klassen sind nichts weiter als Variablen. Für sie gelten die gleichen Kriterien wie für jede andere Variable auch: Benötigt man eine Pointer-Manipulation, braucht man zwangsläufig einen Pointer, benötigt man diese nicht, kann man (muß aber nicht) eine Referenz verwenden.

  • 1
    Antwort von greyhead greyhead

    Einem Zeiger kann man NULL zuweisen, damit er ins Nichts zeigt. Für Referenzen gibt es nichts gleichwertiges. Da steckt immer etwas dahinter (und wenns Schrott ist).

    Dadurch ergibt sich, wann man was anwendet: hat man immer ein gültiges Objekt, ist eine Referenz etwas schöner. Braucht man dagegen die Möglichkeit festzulegen, dass es "nichts" gibt, so ist man mit einem Zeiger besser bedient.

    Kommentar von Monko Monko

    Kann man Referenzen als Klassenvariablen/member deklarieren?

    Kommentar von Mickeyman MickeymanMickeyman

    Ja, das geht. Allerdings musst du die über die Initialisierungsliste des Konstruktors initialisieren.

    Z.B.:

    class Piep

    {

    int& zahl;

    .

    public:

    Piep(int& zahl): zahl(zahl) {}

    };

    .

    Die Zeile mit nur einem Punkt einfach ignorieren. :)

    Kommentar von Monko Monko

    Ok das meinte ich bzw. habe ich irgendwo hier geschrieben... Aber danach kann man diese nicht mehr ändern, oder?

          int neuezahl = 9;
    
        this->zahl = neuezahl;
    
  • 1
    Antwort von DarkIllusion DarkIllusion

    Soweit mir bekannt, gibt es nur die Methoden, Call-by-Reference und Call-by-Value. Also entweder ich übergebe den Pointer(also die Adresse des Wertes) und muss diese Adresse dereferenzieren, um auf den Wert zugreifen zu können, oder ich übergebe den Wert direkt und beim Funktionsaufruf wird eine Kopie erstellt, was wiederum zu Lasten des Stack-Speichers fällt.

    Wann du welche Methode verwendest, hängt meist vom Parameter selbst ab, beispielsweise macht es meistens keinen Sinn, ein Array als Value zu übergeben, denn es würde in der aufgerufenen Funktion ein neues Array erstellt werden, werden dort die Werte des Arrays geändert, hätte das keinen Effekt auf das Ursprungsarray, sondern man müsste das Array zurückgeben, was nicht möglich. Übergibt man stattdessen die Adresse des Array, lassen sich durch Dereferenzierung die Werte im Ursprungsarray direkt ändern. Außerdem ist ein Array wesentlich größer, als ein Pointer, mit nur einer Hexadezimalzahl.

    Kommentar von Monko Monko

    Das war mir schon klar.

    Das "Dilemma" besteht zur Zeit nur aus Zeiger und Pointer. Pass-by-Value ist "steht garnicht mehr zur Auswahl"

    Kommentar von WhiteGandalf WhiteGandalfWhiteGandalf

    Stimmt nicht. In C/C++ gibt es außerdem "by Pointer". Näheres dazu siehe Kommentar weiter unten...

  • 1
    Antwort von Mickeyman Mickeyman

    Am schnellsten ist auf jeden Fall die Referenz. Daher sollte nach Möglichkeit immer auf die Referenz zurückgegriffen werden.

    Das einzige Problem bei Referenzen ist, dass man die nicht abspeichern kann. Z.B. in einer Liste. Dadurch kann es zu fehlerhaften Referenzen während der Laufzeit kommen.

    Was ich kürzlich auch erst herausgefunden habe ist, dass Polymorphie auch dadurch dargestellt werden kann.

    Also als Übergabeparameter kann man eigentlich immer Referenzen statt Zeigern verwenden.

    Kommentar von Mickeyman MickeymanMickeyman

    Vielleicht noch als kurze Begründung, warum Referenzen schneller als Zeiger sind.

    Bei Zeigern muss man immer erst Dereferenzieren, um auf den eigentlich Wert zuzugreifen, was natürlich Zeit benötigt. Bei Referenzen greift man dagegen direkt auf die übergebene Variable zu. Dies wird glaube ich zur Kompilezeit überreicht.

    Kommentar von Monko Monko

    Wieso kann man denn nicht Referenzen in einer Liste speichern, man könnte doch vorher sie in Pointer umwandeln, oder?

    Und wieso nutzen dann eigentlich einige Bibliotheken wie VCL oder wxWidgets Zeiger und nicht Referenzen?

    Kommentar von Mickeyman MickeymanMickeyman

    Leider hat mir das Forum meinen ganzen schönen Kommentar weggeschmissen. :( Daher alles noch mal ...

    .

    Also man kann Zeiger problemlos in Listen speichern. Nur bei Referenzen geht das nicht, da der Compiler dann keine Zuordnung mehr machen kann.

    Warum manche Bibliotheken Zeiger statt Referenzen als Übergabeparameter verwenden, kann ich nicht mit Sicherheit sagen. Höchstwahrscheinlich liegt es an der Polymorphie. Vielleicht wissen/wussten die nicht, dass es auch mit Referenzen möglich ist oder dieses Verhalten ist bei einer früheren C++ Version noch nicht möglich gewesen. Ich weiß es eigentlich auch noch nicht so lange, dass das möglich ist.

    Kommentar von Mickeyman MickeymanMickeyman

    Leider hat mir das Forum meinen ganzen schönen Kommentar weggeschmissen. :( Daher alles noch mal ...

    .

    Also man kann Zeiger problemlos in Listen speichern. Nur bei Referenzen geht das nicht, da der Compiler dann keine Zuordnung mehr machen kann.

    Warum manche Bibliotheken Zeiger statt Referenzen als Übergabeparameter verwenden, kann ich nicht mit Sicherheit sagen. Höchstwahrscheinlich liegt es an der Polymorphie. Vielleicht wissen/wussten die nicht, dass es auch mit Referenzen möglich ist oder dieses Verhalten ist bei einer früheren C++ Version noch nicht möglich gewesen. Ich weiß es eigentlich auch noch nicht so lange, dass das möglich ist.

    Kommentar von Monko Monko

    Und kann man Referenzen als Klassenvariablen/member deklarieren? Denn da hat man ja auch keine direkte Zuordnung oder geht das bei einer direkten Initialisierung über den Konstruktor?

    Kommentar von WhiteGandalf WhiteGandalfWhiteGandalf

    Stimmt nicht.

    Pointer und Referenz sind nur zwei syntaktische Varianten, die im Compilat zu binär identischen Maschinencode führen. (Es sei denn, der Compiler würde spinnen. Das war mal bis zum Anfang der 90er des vergangenen Jahrtausends der Fall, aber inzwischen ist das kalter Kaffee!)

    Kommentar von Mickeyman MickeymanMickeyman

    Ohje, was hast du nur getan. ^^

    Meine schöne Welt zerstört! Habe mich auf Grund deines Kommentars noch mal genauer im Internet umgeschaut und tatsächlich einen Beitrag gefunden, der dein Kommentar untermauert.

    Tut mir Leid Monko, dass ich dir so ein paar Fehlinformationen gegeben habe. Und Danke WhiteGandalf, dass du mich korrigiert hast!

    Hier ist noch der Link, wo man das sehr gut nachlesen kann: http://www.parashift.com/c++-faq-lite/references.html

    Kommentar von Monko Monko

    Ein Weltuntergang^^ Nein nicht so schlimm ;) Vielen Dank für die ganzen Erklärungen.

Diese Frage

Verwandte Fragen

Noch nicht den richtigen Rat gefunden?

Einfach und schnell viele hilfreiche Ratschläge von Deutschlands aktivster Ratgeber-Community erhalten!

Einfach und schnell einen Tipp erstellen und Ihren guten Rat mit anderen teilen!

Einfach und schnell ein Video hochladen und anschaulichen Rat an alle geben!

Die unter gutefrage.net angebotenen Dienste und Ratgeber Inhalte werden nicht geprüft. Die Richtigkeit der Inhalte wird nicht gewährleistet. Rechtliche Hinweise finden Sie hier.