Zum Inhalt springen
Topos Uranos

Zusammenfassung

Diese Lektion untersucht, auf welchen Wegen die Daten in eine Funktion gelangen und ihre Ergebnisse sie verlassen. Standardmäßig übergibt C++ die Argumente als Wert: Der Parameter ist eine Kopie, und nichts von dem, was die Funktion mit ihm macht, erreicht das Original. Eine Referenz dagegen ist ein zweiter Name für eine bereits existierende Zelle und erlaubt der Funktion, die Variable des Aufrufers zu ändern; die konstante Referenz erlaubt, ein großes Datum zu lesen, ohne es zu kopieren und ohne es verändern zu können. Um mehrere Ergebnisse zurückzugeben, bietet die Bibliothek std::pair und std::tuple, die mit strukturierten Bindungen empfangen werden. Es folgen der Gültigkeitsbereich eines Namens (wo er sichtbar ist) und die Speicherdauer einer Variablen (wann sie existiert), mit der Verdeckung eines Namens und den lokalen static-Variablen, und den Abschluss bildet die Referenz, die ihre Variable überlebt. Der Begriff, der alles vereint, ist der Datenfluss zwischen den Teilen eines Programms: Kopieren oder Teilen, und die Sichtbarkeit der Namen als Mechanismus der Abschottung.

Lernziele

  1. Die Wirkung einer Funktion auf ihre Argumente vorhersagen, je nachdem, ob sie als Wert oder als Referenz übergeben werden.
  2. Die Übergabeart jedes Parameters wählen (Wert für kleine Typen, konstante Referenz für große Daten, Referenz für das, was geändert wird) und die Wahl begründen.
  3. Mehrere Ergebnisse mit std::pair oder std::tuple zurückgeben und sie mit strukturierten Bindungen empfangen.
  4. Den Gültigkeitsbereich und die Speicherdauer jeder Variablen eines Programms bestimmen, einschließlich der lokalen static-Variablen und derer, die von einer anderen verdeckt werden.
  5. Die Referenz auf eine lokale Variable oder auf einen temporären Wert, der nicht mehr existiert, erkennen, samt der Warnung des Compilers, die sie ankündigt, und sie korrigieren.

Wertübergabe

Der Parameter ist eine Kopie

Die Lektion Funktionen hat gezeigt, dass jeder Aufruf einen Rahmen auf dem Stapel anlegt, mit den Parametern und den lokalen Variablen der Funktion, und dass der Rahmen beim Rücksprung verschwindet. Es bleibt zu klären, in welcher Beziehung ein Parameter zu dem Argument steht, das ihn initialisiert. Die Standardantwort von C++ ist die einfachste: Der Parameter ist eine neue lokale Variable, die mit einer Kopie des Wertes des Arguments initialisiert wird. Wenn dem so ist, ändert eine Funktion, die ihre Parameter ändert, ihre Kopien, und die Variablen des Aufrufers ändern sich nicht. Das folgende Programm stellt dies mit einer Funktion auf die Probe, die zwei ganze Zahlen zu vertauschen versucht.

C++-Programm
#include <print>​// Versucht, a und b zu vertauschen; vertauscht nur ihre Kopien.void swapValues(int a, int b){    const int saved{a};    a = b;    b = saved;    std::println("Innen:  a = {}, b = {}", a, b);}​int main(){    int x{1};    int y{2};    swapValues(x, y);    std::println("Danach: x = {}, y = {}", x, y);}
Ausgabe
Innen:  a = 2, b = 1Danach: x = 1, y = 2

Innerhalb der Funktion gelingt die Vertauschung; nach dem Aufruf behalten x und y ihre Werte. Der Compiler warnt nicht, denn es gibt nichts zu warnen: Der Code vertauscht zwei lokale Variablen, die am Ende verschwinden. Die Abbildung verfolgt die Rahmen des Stapels während des Aufrufs.

BeispielZwei Rahmen, vier Zellen

Der Rahmen von main enthält x und y; der Aufruf legt darüber den Rahmen von swapValues an, mit eigenen Zellen.

Beweis

  1. Vor dem Aufruf hat der Rahmen von main zwei Zellen: x mit 1 und y mit 2.

  2. Der Aufruf legt den Rahmen von swapValues an. Seine Parameter a und b sind neue Zellen, und in sie werden die Werte der Argumente kopiert: 1 und 2. Von diesem Moment an besteht keine Verbindung mehr zwischen a und x, außer dass sie mit demselben Wert begonnen haben.

  3. Die lokale Variable saved wird im selben Rahmen angelegt und speichert eine Kopie von a.

  4. Die beiden Zuweisungen vertauschen a und b. Die Zellen, die sich ändern, liegen alle im oberen Rahmen; die von main werden nicht berührt.

  5. Beim Rücksprung verschwindet der Rahmen von swapValues vollständig, mit der Vertauschung, die er enthielt. x und y haben weiterhin die Werte 1 und 2.

Was die Kopie schützt

Die Wertübergabe ist kein Mangel, sondern die Regel, die Funktionen vernünftig macht. Erstens kann das Argument ein beliebiger Ausdruck sein, nicht nur eine Variable: swapValues(x + 1, 7) ist gültig, weil der Parameter mit einem Wert initialisiert wird, woher er auch kommt. Zweitens weiß der Aufrufer aus der Deklaration, ohne den Rumpf zu lesen, dass sich seine Variablen nicht ändern werden. Drittens ist der Parameter innerhalb der Funktion eine weitere lokale Variable, die ohne Folgen nach außen als Arbeitsvariable dienen kann.

C++-Programm
#include <print>​// Anzahl der Dezimalziffern von n.// Vorbedingung: n >= 0.// Nachbedingung: gibt die Anzahl der Ziffern von n zurück (1 für die 0).int digitCount(int n){    int count{1};    while (n >= 10) {        n = n / 10;        ++count;    }    return count;}​int main(){    const int population{48'210};    std::println("{} hat {} Ziffern", population, digitCount(population));}
Ausgabe
48210 hat 5 Ziffern

Die Funktion zerstört ihren Parameter, indem sie ihn durch 10 teilt, bis er nur noch eine Ziffer hat; da er eine Kopie ist, bleibt population unversehrt und kann const sein. Daraus folgt eine Leseregel, die für den ganzen Kurs gilt: Wenn ein Parameter nicht das Zeichen & trägt, das im nächsten Teil dieser Lektion untersucht wird, kann die Funktion die Variable des Aufrufers nicht ändern, was immer sie mit dem Parameter tut.