Referenz (Programmierung)

Eine Referenz ist ein Verweis auf ein Objekt. Eine Referenz ist damit ein Aliasname für ein bereits bestehendes Objekt.

Definitionen

Ein Zeiger ist eine in einer Variablen abgelegte Speicheradresse. Über diese kann das durch sie referenzierte Datenobjekt zugegriffen werden. Technisch ist der Zeiger die einzige Möglichkeit, nicht-absolut auf ein im Hauptspeicher befindliches Datenobjekt zu verweisen. In höheren Programmiersprachen tauchen Zeiger meist implizit auf. In der Zuweisung a = a + 1 ist zum Beispiel mit dem linken a die Speicheradresse der Variablen a und mit dem rechten a ihr Wert gemeint (Wodurch dieser mathematisch falsche Ausdruck überhaupt erst Sinn ergibt). Bei expliziter Verwendung wird die Speicheradresse in einer (Zeiger-)Variablen abgelegt und kann über diese zum Zugriff auf das Datenobjekt verwendet werden.

Ein Handle ist eine abstrakte Referenz und kann auf verschiedene Arten dargestellt werden. Ein häufiges Beispiel sind Datei-Handles, bei denen der zugehörige Metadatensatz (Dateideskriptor) geöffneter Dateien durch eine ganzzahlige Losnummer identifiziert wird. Das Betriebssystem erwartet bei allen Handhabungen dieser geöffneten Datei (Lesen, Schreiben, Schließen etc.) die Angabe der zugehörigen Losnummer.

Bei verteilten Systemen kann die Referenz mehr als eine Speicheradresse oder einen Bezeichner enthalten. Es kann auch eine eingebettete Spezifikation der Netzwerkprotokolle enthalten, die zum Lokalisieren und Zugreifen auf das referenzierte Objekt verwendet werden, sowie die Art und Weise, wie Informationen codiert oder serialisiert werden. So kann beispielsweise eine WSDL-Beschreibung eines Remote-Webservices als Referenzform angesehen werden. Es enthält eine vollständige Spezifikation zum Auffinden und Binden eines bestimmten Webdienstes. Ein Verweis auf ein verteiltes Live-Objekt ist ein weiteres Beispiel: Es handelt sich um eine vollständige Spezifikation zum Erstellen einer kleinen Softwarekomponente, die als Proxy bezeichnet wird und anschließend eine Peer-to-Peer-Interaktion durchführt und über die der lokale Computer möglicherweise Zugriff erhält Daten, die repliziert werden oder nur als schwach konsistenter Nachrichtenstrom existieren. In all diesen Fällen enthält die Referenz den vollständigen Satz von Anweisungen oder ein Rezept für den Zugriff auf die Daten. In diesem Sinne dient es demselben Zweck wie ein Bezeichner oder eine Speicheradresse.

Referenzen in C++

In der Programmiersprache C++ werden Referenzen sehr häufig und für verschiedene Zwecke eingesetzt:

  1. als (kürzerer oder verständlicherer) Aliasname für ein bereits bestehendes Objekt
  2. zur Optimierung, um Kopien von Objekten zu vermeiden
  3. in speziellen Memberfunktionen, wie Copy- & Move-Konstruktoren und Zuweisungsoperatoren
  4. als sogenannte universelle Referenz (engl.: universal reference), die bei Templates einen beliebigen Parametertyp repräsentiert.

Es gibt in C++ sogenannte Lvalue-Referenzen, die durch ein an den Typ angehängtes & gekennzeichnet werden, und (seit C++11) zusätzlich noch Rvalue-Referenzen, die durch && gekennzeichnet werden.

Codebeispiele

Unterschied von Referenz und Kopie:

int original = 5;
int kopie = original;
int& referenz = original;

kopie    = 30;  // weist der Kopie den Wert 30 zu. Das Original bleibt unverändert
referenz = 20;  // weist der Referenz – und somit auch dem Original – den Wert 20 zu
original = 10;  // ändert das Original, womit aber auch die Referenz ihren Wert ändert.

Parameterübergabe als Referenz:

void quadrieren(int& x) {
    x = x * x;
}

int main() {
    int i = 5;
    quadrieren(i);  // Funktionsaufruf ändert den Wert von i auf 25
}

Objektreferenzen:

Bank& nBank = Bankenverzeichnis::nachBLZ("76543210");  // eine Referenz auf ein Bankobjekt wird beschafft
Konto& nKonto1 = nBank.kontoZugriff("1234567");        // eine Referenz auf ein bestimmtes Kontoobjekt wird beschafft
Konto& nKonto2 = nBank.kontoZugriff("1111111");        // eine Referenz auf ein weiteres Kontoobjekt wird beschafft
nKonto1.einzahlung(100.00, "EUR", nKonto2);            // eine Methode wird auf nKonto1 gerufen

Klassendesign:

class Kunde
{
public:
    explicit Kunde(const std::string& name);  // 'name' wird aus Effizienzgründen nur als const-Referenz übergeben
    explicit Kunde(std::string&& name);       // Rvalue-Referenz, erlaubt ein "move" aus dem Namen (seit C++11)

    Kunde(const Kunde& other);  // Copy-Konstruktor
    Kunde(Kunde&& other);       // Move-Konstruktor (seit C++11)

    const std::string& getName() const;  // gibt const-Referenz auf Kundennamen zurück
    std::string&& getName() &&;  // gibt Rvalue-Referenz zurück, falls Objekt selbst ein RValue ist (ab C++11)

};

Beispiel in Pascal

Übergabe by reference (der Wert der übergebenen Variablen wird geändert)

procedure quadriere(var wert: integer);
begin
    wert := wert * wert;
end;

Dieser Prozedur kann nur eine Variable übergeben werden, kein Ausdruck. wert ist der lokale Name der als Referenz übergebenen Variablen. Mit der Zuweisung eines Werts wird direkt der Inhalt der übergebenen Variablen geändert.

Übergabe by value (also nur des Werts, nicht der Variablen selbst; der Wert der übergebenen Variablen wird nicht geändert)

function quadrat(wert: integer): integer;
begin
    quadrat := wert * wert;
end;

Das Ergebnis wird als Rückgabewert der Funktion an das Hauptprogramm zurückgegeben. Die Rückgabe erfolgt durch eine Zuweisung an den Namen der Funktion.
In anderen Programmiersprachen wird dagegen return x * x; verwendet.

Selbst wenn innerhalb der Funktion eine Zuweisung an wert erfolgen würde, würde dies den Inhalt einer etwa übergebenen Variablen nicht ändern: Übergeben wird nur ein Wert. Der Bezeichner wert steht für eine lokale Variable, die nur innerhalb der Funktion gültig ist.