[an error occurred while processing this directive]

Einführung in Referenzdefinitionen in C++ im Rahmen der Lehre des Programmierens. [] (C++, Referenz, Referenzen, Referenzvariablen, Referenzparameter, Referenzargumente, Referenz-Variablen, Referenz-Parameter, Referenz-Argumente), Lektion, Seite 720853
http://www.purl.org/stefan_ram/pub/c++_referenzdefinition_de (Permalink) ist die kanonische URI dieser Seite.
Stefan Ram

Referenzen in C++

Objektnamensdeklarationen in C++

In dem folgenden Beispielprogramm wird zunächst eine Variable in der klassischen Weise definiert. Bei einer solchen Variablendefinition wird automatisch immer auch ein neues  Objekt angelegt.

Es folgt dann die Definition eines reinen Objektnamens. Hierbei wird dem neuen Namen das Zeichen "&" vorangestellt.

Bei einer reinen Objektnamensdeklaration wird kein  neues Objekt angelegt, sondern nur ein weiterer Name für ein schon vorhandenes Objekt vereinbart.

Daher muß bei einer Objektnamensdeklaration immer ein schon vorhandenes Objekt angegeben werden, an das dann (auch) der neue Name gebunden wird. In dem folgenden Beispiel wird der neue Name "w" an das Objekt der schon bestehenden Variablen "v" gebunden.

Danach ist der Name "w" an dasselbe  Objekt gebunden wie zuvor schon der Name "v". Nun sind die Variable "v" und die Variable "w" beide Variablen mit dem gleichen  Objekt, die sich nur noch in ihrem Namen unterscheiden, aber sonst absolut gleichberechtigt  sind.

Wurde durch eine Variablendefinition oder eine Objektnamensdeklaration ein Name einmal an ein Objekt gebunden, so gilt diese Bindung danach unabänderlich im ganzen Gültigkeitsbereich des Namens. Derselbe Name kann also in seinem Gültigkeitsbereich (während derselben Lebenszeit) nicht an ein anderes Objekt gebunden werden. Hierin unterscheiden sich die durch eine Objektnamensdeklaration eingeführten Namen nicht von den bisher schon bekannten Variablennamen.

Die folgende Zeilen zeigen dann, daß es daher im folgenden egal ist, wann man die Variable "v" und wann die Variable "w" verwendet. Sie unterscheiden sich jetzt nur in dem Namen, aber nicht in ihrem Objekt. Man kann auch sagen, der Bezeichner "w" sei nun ein Synonym  für den Bezeichner "v". Die Bezeichner sind, abgesehen davon, daß der eine »w« und der andere »v« lautet, absolut gleichberechtigt.

referenzen.cpp
#include <iostream> 
#include <ostream>
int main() 
{ std::cout << "Objektnamensdefinition\n"; 
int v( 12 ); /* Variablendefinition und Initialisierung */ 
int & w( v ); /* Namensdeklaration von w zum Objekt von v */ 
std::cout  
<< & v << '\n' /* Ausgabe der Objektkennzahl (Adresse) */ 
<< & w << '\n' /* Ausgabe der Objektkennzahl (Adresse) */ 
<< w << '\n' /* Ausgabe des Objektwertes */ 
<< v << '\n'; /* Ausgabe des Objektwertes */ 
w = 7; /* Das Objekt wird auf 7 gesetzt. */ 
std::cout  
<< w << '\n' /* Ausgabe des Objektwertes */ 
<< v << '\n'; /* Ausgabe des Objektwertes */ 
v = 4; /* Das Objekt wird auf 4 gesetzt. */ 
std::cout  
<< w << '\n' /* Ausgabe des Objektwertes */ 
<< v << '\n'; /* Ausgabe des Objektwertes */ 
}

std::cout
Referenzen 
006BFDF4 
006BFDF4 
12 
12 



4

Man beachte den Alias-Effekt: Ein Schreibzugriff über den Namen "w" verändert nicht nur den über den Namen "w" erreichbaren Wert, sondern auch den über den Namen "v" erreichbaren Wert, da beide Namen sich ein Objekt teilen.

Da die Adressen (Objekte) beider Variablen gleich sind, kann man den Adreßoperator in der Objektnamensdefinition "& w = v" auch lesen als „die Adresse (d.h.: das Objekt) der Variablen "w" soll die von (dem Objekt von) der Variablen "v" sein.“

Nach der Definition von v [UML static structure diagram]
                                    .----------------------. 
| 12 : int | 
| -------- | 
| | 
'----------------------' 

| hat Wert ^ 

.--------------. .----------------------. 
| v : Name | gebunden an > | 006BFDF4: int-Objekt | 
| -------- |------------------->| -------------------- | 
| | | | 
'--------------' '----------------------'

Nach der Definition von w [UML static structure diagram]
                                    .----------------------. 
| 12 : int | 
| -------- | 
| | 
'----------------------' 

| hat Wert ^ 

.--------------. .----------------------. 
| v : Name | gebunden an > | 006BFDF4: int-Objekt | 
| -------- |------------------->| -------------------- | 
| | | | 
'--------------' '----------------------' 
.--------------. ^ 
| w : Name | gebunden an > | 
| -------- |--------------------------------' 
| | 
'--------------'

Nach der Definition von v [Behaelterdiagramm]
   .                       . 
|-------. _____ Wert | Objekt 
| v |' '-. | 
|-------'# ## '. | 
| / ## # # \ | 
| ; # # # ; | 
| | # # | | 
| ; # # ; | 
| \ # # / | 
' '. ### ######.' ' 
\ '-._____.-' / 
'-------------------'

Nach der Definition von w [Behaelterdiagramm]
   .                       . 
|-------. _____ Wert | Objekt 
| v |' '-. | 
|-------'# ## '. | 
|-------.# # # \ | 
| w |# # # ; | 
|-------'# # | | 
| ; # # ; | 
| \ # # / | 
' '. ### ######.' ' 
\ '-._____.-' / 
'-------------------'

Namen, die über eine Namensdefinition an ein schon vorhandenes Objekt gebunden werden, nennt man auch „Referenzen“, dementsprechend wird die Objektnamensdefinition auch als „Referenzdefinition“ bezeichnet. Der Name „Referenz“ ist hier allerdings irreführend, wenn man dadurch verleitet wird, in einer Referenz etwas anderes zu sehen als in einer Variablen. Das letzte Programmbeispiel zeigte ja, daß ein durch eine Referenzdeklaration eingeführter Name nicht von einem zuvor durch eine Variablendefinition eingeführten Namen zu unterscheiden ist. Daher ist es besser, beide Namen als Objektnamen anzusehen, die sich nur in der Art ihres Zustandekommens unterscheiden, aber absolut gleichberechtigt sind, nachdem sie einmal gebildet wurden.

Da sich ein über eine Referenzdefinition eingeführter Name danach wie eine zusammen mit einem Objekt definierte Variable verhält, werden in diesem Text Referenzen auch manchmal als „Variablen“ bezeichnet. Eine Variable kann umgekehrt als eine Referenz auf ihr Objekt angesehen werden. Eine Referenz unterscheidet sich von einer anderen Variablen nur durch ihre Entstehung, aber nicht durch die Eigenschaften, die sie danach im Gebrauch hat. Allerdings können einer Referenz durch ausdrückliches Verlangen noch spezielle Eigenschaften gegeben werden, wie dies im folgenden Abschnitt beschrieben wird.

Objektnamen mit Nur-Lese-Zugriff

Variablen können sich in ihren Eigenschaften  unterscheiden. In dem folgenden Beispiel wird die Variable "w" mit dem Schlüsselwort "const" vor dem Und-Zeichen "&" definiert. Das bedeutet, daß durch die Variable "w" keine Schreibzugriffe  auf das Objekt möglich sein sollen. Anstatt dieser genauen Formulierung wird auch die Bezeichnung „konstante Referenz“ verwendet. Die Bezeichnung „konstante Referenz“ ist also immer so zu verstehen, daß über die verwendete Referenz  keine Schreibzugriffe auf das Objekt möglich sind. (Es ist nicht  so gemeint, daß die Referenz in dem Sinne konstant sei, daß sie nicht auf ein anderes Objekt geändert werden könne. Das ist zwar auch richtig, ist aber bei Variablen immer so. Es ist auch nicht notwendigerweise eine Referenz auf ein konstantes Objekt, weil das Objekt durch andere [nichtkonstante] Referenzen, ja durchaus noch verändert werden könnte, also nicht konstant sein muß.)

Im folgenden Programmbeispiel kann die Variable "w" wie bisher verwendet werden, um den Wert des Objektes zu bezeichnen. Es ist aber nicht  mehr möglich, den Wert des Objektes über die Variable "w" zu verändern. Da dies aber eine Eigenschaft der Variablen "w" und nicht des Objektes selber ist, ist es über die Variable "v" weiterhin möglich, den Wert des Objektes zu verändern.

referenzen2.cpp
#include <iostream> 
#include <ostream>
int main() 
{ std::cout << "Referenzen\n"; 
int v( 12 ); /* Variablendefinition und Initialisierung */ 
const int & w( v ); /* Namensdefinition von w zum Objekt von v */ 
v = 7; /* Das Objekt wird auf 7 gesetzt. */ 
std::cout  
<< w << '\n' /* Ausgabe des Objektwertes */ 
<< v << '\n'; /* Ausgabe des Objektwertes */ 
/* w = 4; */ /* Nicht erlaubt, da w nur fuer Lesezugriff */ }

Die wesentlichen Aspekte der Situation nach der Definition der Variablen "w" werden in dem folgenden Schaubild dargestellt.

Nach der Definition von w [UML static structure diagram]
                                        .----------------------. 
| 12 : int | 
| -------- | 
| | 
'----------------------' 

| hat Wert ^ 

.--------------. .----------------------. 
| v : Name | Referenz > | 006BFDF4: int-Objekt | 
| -------- |----------------------->| -------------------- | 
| | Lesen und Schreiben | | 
'--------------' '----------------------' 
.--------------. ^ 
| w : Name | konstante Referenz > | 
| -------- |------------------------------------' 
| | ueber diese Bindung nur Lesen 
'--------------'

Das folgende Diagramm stellt dieselbe Situation etwas ausführlicher dar.

Referenz [UML, static structure diagram]
.--------.                                            .--------. 
| v:Var- | .----------. .----------. | w:Var- | 
| iable | * 1 | Eigen- | | const&: | 1 * | iable | 
| ------ |<>------| schaft- | | Eigen- |------<>| ------ | 
| | | ten | | schaft | | | 
| | '----------' '----------' | | 
| | .----------. .----------. | | 
| | 1 1 | v : Name | | w : Name | 1 1 | | 
| |<>------| -------- | | ------- |------<>| | 
| | | | | | | | 
| | '----------' '----------' | | 
| | | | | | 
| | | * | * | | 
| | '----. .----' | | 
| | V Referenz | | const Referenz V | | 
| | | | | | 
| | | 1 | 1 | | 
| | V V | | 
| | .-----------. | | 
| | * 1 | O:Objekt | 1 * | | 
| |--------------->| -------- |<--------------| | 
| | | | | | 
| | '-----------' | | 
| | | | | | | 
'--------' .--------------' | '--------------. '--------' 
| 1 | 1 | 1 
v v v 
.---------. .---------. .---------. 
| int:Typ | | Wert | | Eigen- | 
| ------- | | | | schaf- | 
| | | | | ten | 
'---------' '---------' '---------'

C++  überwacht den Zugriffschutz, den eine konstante Referenz bietet: Es ist zwar möglich von einer Referenz eine konstante Referenz abzuleiten, aber nicht umgekehrt von einer konstanten Referenz eine (nicht-konstante) Referenz, da dieses es erlauben würde, den Zugriffsschutz zu umgehen.

referenzen2c.cpp
int main() 
{ int v( 2 ); /* Variablendefinition und Initialisierung */ 
int & r( v ); /* Referenzdefinition */ 
const int & c( r ); /* konstante Referenzdefinition */ 
int & u( c ); /* nicht erlaubt */ }

Fehlermeldungen
line 5: error: conversion from `const int' to `int &'  
discards qualifiers 
qualifiers dropped in binding reference of type "int &"  
to initializer of type "const int"

Es ist natürlich nicht möglich, durch eine Referenz einen Schreibzugriff auf eine Konstante zu erhalten, da dies keinen Sinn ergeben würde, denn nur Objekte (Wertspeicher) können überhaupt verändert werden, keine Werte. In dem folgenden Programm wird eine Konstante "k" definiert. Zu solch einer Konstanten muß ja gar kein Objekt existieren. Demzufolge kann sie auch nicht zur Definition einer „weiteren Referenz“ auf ein Objekt verwendet werden.

Dies folgt der allgemeinen Regel, daß man ein veränderliches Objekt immer als Wert auffassen darf (nämlich als den momentanen Wert des Objektes), aber es umgekehrt nicht erlaubt ist, einen Wert als zeitlich veränderbares Objekt (als einen Wertspeicher) zu interpretieren.

referenzen3.cpp
#include <iostream> 
#include <ostream>
int main() 
{ std::cout << "Referenzen\n"; 
const int k( 2 ); /* Definition einer Konstanten */ 
int & w( k ); /* Nicht erlaubt */ }

Die Referenzdefinition in dem folgenden Beispiel ist offensichtlich nicht erlaubt, da der Wert "2" kein Objekt ist.

referenzen4.cpp
#include <iostream> 
#include <ostream>
int main() 
{ int & v( 2 ); /* Nicht erlaubt */ }

Nachdem nun so plastisch geschildert wurde, daß zu einem Wert kein Objekt gebildet werden kann, muß nun angefügt werden, daß es von dieser Regel in C++  doch auch eine Ausnahme  gibt.

C++  erlaubt es nämlich einen Rechtswert  zur Initialisierung von konstanten Referenzen zu verwendet. Da ein Rechtswert aber im allgemeinen nicht verändert werden kann, muß dann auch die Referenz konstant sein, also eine Referenz auf ein konstantes Objekt sein, aber immerhin auf ein Objekt. Woher soll aber solch ein konstantes Objekt kommen? Es wird in diesem Fall von C++  automatisch ein neues Objekt angelegt und mit dem gegebenen Rechtswert initialisiert! Dieses Objekt wird dann an die neue Variable gebunden.

referenzen5.cpp
#include <iostream> 
#include <ostream>
int main() 
{ const int & v( 2 ); /* erlaubt */ 
std::cout  
<< &v << '\n' 
<< v << '\n'; 
const int & w( 2 ); /* erlaubt */ 
std::cout  
<< &w << '\n' 
<< w << '\n'; }

std::cout
006BFDF0 

006BFDE8 
2

Hier wurden zwei Objekte erzeugt und mit dem Wert "2" initialisiert.

Gibt es auch Referenzen auf Referenzen?

Eine sogenannte Referenz ist nichts weiter als eine Variable. Sie unterscheidet sich von anderen Variablen nur durch die Art der Entstehung ihrer Bindung: Bei einer Variablendefinition  wird ein neuer  Name an ein neues  Objekt gebunden, bei einer Referenzdefinition  wird ein neuer  Name an ein schon existierendes  Objekt gebunden.

Wird nun eine weitere Referenz unter Verwendung einer Referenz erstellt, so ist dies nur eine weitere Referenz auf das Objekt der vorgegebenen Referenz. Dies kann man schon daran erkennen, daß eine Referenz zum Objekt einer Variablen danach der ursprünglichen Variablen gleichzusetzen ist (abgesehen von Einschränkungen wie dem Nur-Lese-Zugriff). Daher ist eine Referenz, die unter Verwendung einer Referenz gebildet wird, nichts anderes als eine Referenz, die unter Verwendung einer Variablen mit demselben Objekt gebildet wird. Insofern gibt es also immer nur Referenzen auf Objekte, aber keine Referenzen auf Referenzen (oder Variablen).

referenzen6.cpp
#include <iostream> 
#include <ostream>
int main() 
{ int v( 4 ); /* Erzeugung eines Objekts, Bindung des Namens "v" */ 
int & w( v ); /* Bindung des Namens "w" an dasselbe Objekt */ 
int & u( w ); /* Bindung des Namens "u" an dasselbe Objekt */ 
std::cout  
<< &v << ", " << v << '\n' 
<< &w << ", " << w << '\n' 
<< &u << ", " << u << '\n'; }

std::cout
0x254fdd4, 4 
0x254fdd4, 4 
0x254fdd4, 4

Rückgabe von Referenzen

Die Lebenszeit von Parametern und lokalen Variablen in Funktionsinkarnationen endet (mit einer Ausnahme) mit dem Ende der Lebenszeit der Funktionsinkarnation. Daher ist es nicht sinnvoll, Referenzen auf solche Objekte aus einer Funktion zurückzugeben. Denn wenn der Aufrufer die erhaltene Referenz verwenden kann, ist das Objekt schon erloschen.

Das folgende Programm deklariert die Rückgabe einer Referenz  auf ein Objekt vom Typ "double". Dadurch bewirkt die Anweisung "return result;" nicht die Rückgabe des Wertes  (Rechtswertes) des Objektes "result" sondern die Rückgabe einer Referenz auf das Objekt "result".

referenzreturn.cpp
#include <iostream> 
#include <ostream>
double & kelvin( double const celsius ) 
{ double result( celsius + 273.15 );  
return result; }
int main(){ std::cout << kelvin( 22.0 )<< '\n'; }

Die Inkarnation einer Funktion ist ein Behälter für alle lokalen Definitionen der Funktion. Daher endet die Lebenszeit der lokalen Objekte mit der Inkarnation der Funktion. So endet auch die Lebenszeit der Objektes "result" mit dem Ende der Funktionsinkarnation der Funktion "kelvin". Die zurückgegebene Referenz ist für den Klienten der Funktion also bedeutungslos und darf nicht verwendet werden, weil sie eine Referenz auf ein nicht mehr existentes Objekt darstellt.

Rückgabe einer Referenz auf nicht mehr existentes Objekt
 |Zeit    main 
| .-------------------. 
| | | 
| | | 
| | << kelvin( 22.0 ) | double & kelvin 
| | | .--------------------. 
| | | | ( double const | 
| | '-------> celsius ) | 
| | | { double | Anfang der 
| | | result ( | Lebenszeit von 
| | | celsius + 273.15 );| "result" 
| | | | 
| | Referenz auf das | return result;--. | 
| | Objekt "result" | } | | Ende der 
| | .-----------------------------' | Lebenszeit von 
| | | '--------------------' "result" 
| | | | 
| | V | 
| | << kelvin( 22.0 ) | 
| | | 
| | | 
| | | Fehler: Bezug auf nicht mehr 
| | | existentes Objekt. 
V '-------------------'

In bestimmten Fällen kann die Rückgabe einer Objektreferenz sinnvoll sein (nämlich dann, wenn das Objekt noch weiterexistiert). Man muß aber grundsätzlich immer sicherstellen, daß nur Referenzen auf existierende Objekte verwendet werden.

Ein solcher Fall kann gegeben sein, wenn eine Funktion ein vorübergehendes Objekt zurückgibt, das an einen Referenzparameter gebunden ist. Dessen Existenz ist während der Auswertung des den Funktionsaufrufs enthaltenden Vollausdrucks garantiert. Von dieser Garantie sollte aber nur mit Vorsicht Gebrauch gemacht werden, weil anscheinend harmlose Änderungen an solch einem Vollausdruck dann zu Fehlern führen können.

Referenzparameter

Wie bisher beschrieben, kann eine Variable  durch eine Referenzdefinition  so initialisiert werden, daß sie ein schon vorhandenes Objekt verwendet. Auch ein Parameter  kann so initialisiert werden, wenn er als Referenzparameter  definiert wird.

referenzparameter.cpp
#include <iostream> 
#include <ostream>
void ausgabe( const int & parameter ) 
{ std::cout <<  
&parameter << ':' <<  
parameter << '\n'; }
int main() 
{ int o = 7; std::cout << &o << '\n'; 
ausgabe( o ); }

std::cout
006BFDF4 
006BFDF4:7

Der Wert  eines Argumentes steht in der erzeugten Inkarnation der aufgerufenen Funktion als Wert des Parameters zur Verfügung. Daher wird der Wert "7" aus dem Objekt "o" nach dem Aufruf "ausgabe( o )" der Funktion "ausgabe" zugänglich sein.

Wenn ein Parameter einer Funktion als Referenzparameter deklariert wurde, dann wird für diesen Parameter bei Aufrufen der Funktion ein besonders Übergabeverfahren eingesetzt: Die Referenzübergabe.

Bei der Referenzübergabe wird Information über das Argumentobjekt  (und nicht nur sein Wert) an die Inkarnation der Funktion übergeben. Der Aufruf »ausgabe( o )« übergibt also Information über das ganze Objekt "o" an die Funktion »ausgabe«. Das sieht man auch daran, daß Adressen und  Wert des Objektes der Funktion im Programm »referenzparameter.cpp« bekannt sind.

referenzparameter1.cpp
#include <iostream> 
#include <ostream>
void ausgabe( const int & parameter ) 
{ std::cout <<  
&parameter << ':' <<  
parameter << '\n'; }
int main() 
{ ausgabe( 7 ); }

std::cout
006BFDF4:7

Wenn die Referenz konstant  ist, kann auch ein Wert („Rechtswert“) als Argument angegeben werden, zu dem dann automatisch ein vorübergehendes Objekt erzeugt wird. Die Veränderung  eines vorübergehenden anonymen Objektes ergäbe keinen Sinn, weil auf solch ein Objekt nach der ersten Verwendung kein weiterer Zugriff mehr möglich ist. Wenn ein Parameter für eine konstante  Referenz steht, dann besagt dies, daß über diesen Parameter auch gar keine Veränderungen des Objektes erfolgen sollen. Daher ist es dann möglich, ein vorübergehendes Objekt für diesen Parameter zu verwenden.

Der Aufruf "ausgabe( 7 )" wäre bei einem Parameter vom Typ einer nicht-konstanten Referenz nicht möglich, da der Ausdruck "7" kein Objekt  notiert, sondern einen Wert (ein Wert kann nicht verändert werden). Ist der Referenzparameter jedoch konstant, so wird ein vorübergehendes Objekt  erzeugt, mit dem Wert "7" initialisiert und vorübergehend (während der Inkarnation der Funktion "ausgabe") für den Parameter verwendet. Das geschieht genau so, wie es auch bei einer durch eine konstante Referenz erzeugten Variablen geschehen würde, die beispielsweise mit "const int & v( 2 );" definiert wird.

Bei größeren Objekten würde es viel Zeit kosten, wenn deren Wert an eine Funktion übergeben werden würde, weil ein Wert eines größeren Objektes viel Speicher umfassen kann, der dabei kopiert  werden müßte. Daher ist es in C++  üblich, größere Objekte als Referenz zu übergeben. Damit dann aber auch weiterhin Rechtswerte als Argument angegeben werden können, wird dann oft eine konstante  Referenz als Parameter verwendet.

referenzparameter2.cpp
#include <iostream> 
#include <ostream>
void ausgabe( const int & parameter ) 
{ std::cout <<  
&parameter << ':' <<  
parameter << '\n'; }
int main() 
{ int o = 7; std::cout << &o << '\n'; 
ausgabe( o );  
ausgabe( o + 1 ); }

std::cout
006BFDF4 
006BFDF4:7 
006BFDF0:8

Auch der Ausdruck "o + 1" im Programm »referenzparameter2.cpp« ist kein Objekt. Trotzdem kann auch er als Argument einer Funktion mit einem konstanten Referenzparameter verwendet werden, denn es wird wieder ein vorübergehendes Objekt erzeugt und mit dem Wert des Ausdrucks initialisiert.

Nichtkonstante Referenzparameter

Ein Referenzparameter kann auch nichtkonstant  sein. In diesem Fall gilt alles vorher Gesagte, nur daß dann als Argument nur Objekte  („Linkswerte“) in Frage kommen. Es werden dann keine vorübergehenden Objekte erzeugt.

referenzparameter3.cpp
#include <iostream> 
#include <ostream>
void ausgabe( int & parameter ) 
{ std::cout <<  
&parameter << ':' <<  
parameter << '\n'; }
int main() 
{ int o = 7; std::cout << &o << '\n'; 
ausgabe( o ); /* Übergabe des Objektes o */ 
/* ausgabe( 7 ); */ /* nicht erlaubt */ }

std::cout
006BFDF4 
006BFDF4:7

Bei einem nichtkonstanten Referenzparameter ist es aber auch möglich, daß eine Funktion den Wert in einem Argumentobjekt verändert. In solch einem Fall kann die Funktion aber nicht die Adresse oder den Datentyp des Objekts ändern, sondern nur den Wert (Inhalt) des Objekts. Da die Funktion dann aber keine Kopie des Wertes sondern eine Referenz auf das Argumentobjekt selber hat, kann sie dessen Wert auch dauerhaft veränderndie Veränderung bleibt nach dem Ende der Inkarnation der Funktion bestehen.

referenzparameter4.cpp
#include <iostream> 
#include <ostream>
void eight( int & parameter ){ parameter = 8; }
void show( const int & i ) 
{ std::cout << &i << ':' << i << '\n'; }
int main() 
{ int o = 7; show( o ); eight( o ); show( o ); }

std::cout
006BFDF4:7 
006BFDF4:8

Ein Problem kann es werden, wenn der Benutzer einer Funktion sich nicht bewußt ist, daß eine Funktion den Wert eines bestimmten Argumentes verändern kann. Daß dies geschieht, ist nämlich eher selten. Manche sehen dies als ein Argument gegen Referenzparameter an. Andere weisen darauf hin, daß der Benutzer einer Funktion deren Spezifikation kennen sollte und dieser ja entnehmen kann, ob die Funktion ein bestimmtes Argumentobjekt verändern könnte.

Variablenparameter

Bei einem konstanten Referenzparameter  wird ein Objekt übergeben. Wird ein Rechtswert  als Argument angegeben, dann wird ein vorübergehendes Objekt  erzeugt.

Es ist auch möglich, immer  ein vorübergehendes Objekt für einen Parameter zu erzeugen. In diesem Fall wird der Parameter nicht wie eine Referenz, sondern wie eine Variable definiert. Genau wie bei einer Variablendefinition, wird dann immer ein neues Objekt  für den Parameter erzeugt und der Wert  des Argumentes wird in dieses neue Parameterobjekt kopiert. Da hierbei nur der Wert übergeben wird und nicht das gesamte Objekt wird diese Art der Parameterübergabe im Englischen auch als “by value ” (mit Wertübergabe) bezeichnet. Das Parameterobjekt kann dann in der Funktion verändert werden, aber dies hat keine Auswirkungen auf den Wert des Argumentobjekts.

parameter.cpp
#include <iostream> 
#include <ostream>
void ausgabe( int parameter ) 
{ std::cout <<  
&parameter << ':' <<  
parameter << '\n'; 
parameter = 8;  
std::cout << parameter << '\n'; }
void show( const int & i ) 
{ std::cout << &i << ':' << i << '\n'; }
int main() 
{ int argument( 7 ); show( argument ); 
ausgabe( argument ); show( argument ); }

std::cout
006BFDF4:7 
006BFDA4:7 

006BFDF4:7

Ein Referenzparameter  bezieht sich direkt auf das beim Aufruf angegebene Objekt.

Referenzparameter
.--------------------. 
| Argumentobjekt | Referenz auf Argumentobjekt 
| |<------------------------------. 
| | | 
'--------------------' | 
| | 
| | 
| | 
.--------------------. .--------------------. 
| Aufruf | | Referenzparameter | 
| | | | 
| | | | 
'--------------------' '--------------------'

Für einen Variablenparameter  wird bei der Inkarnation einer Funktion ein vorübergehendes Parameterobjekt erzeugt, der Wert des Argumentobjektes wird daraufhin in das Parameterobjekt kopiert. Am Ende der Inkarnation wird das vorübergehende Parameterobjekt wieder aufgelöst.

Variablenparameter
.--------------------.                    .--------------------. 
| Argumentobjekt | | Parameterobjekt | 
| |------------------->| - Erzeugung | 
| | Kopieren des | - Vernichtung | 
'--------------------' Wertes '--------------------' 
| ^ 
| | 
| | 
.--------------------. .--------------------. 
| Aufruf | | Variablenparameter | 
| | | | 
| | | | 
'--------------------' '--------------------'

Konstante Variablenparameter

Oft ist es sinnvoll, den Werte eines Variablenparameters nicht zu verändern, da dieser eine von außen in die Inkarnation der Funktion gegebene Information darstellt. Dann empfiehlt es sich diesen Parameter mit »const« zu kennzeichnen, wodurch dessen Wert nicht mehr verändert werden kann.

constparameter.cpp
#include <iostream> 
#include <ostream>
void ausgabe( const int parameter ) 
{ std::cout <<  
&parameter << ':' <<  
parameter << '\n'; }
void show( const int & i ) 
{ std::cout << &i << ':' << i << '\n'; }
int main() 
{ int o = 7; show( o ); 
ausgabe( o ); show( o ); }

std::cout
006BFDF4:7 
006BFDA4:7 
006BFDF4:7

Wahl der Parameter-Art

Primitive Zahlenobjekte und Zahlenwerte brauchen relativ wenig Speicherplatz, sind leicht zu erzeugen und aufzulösen. Die Verwaltung eines Referenzparameters erfolgt intern meistens durch eine Objektadresse, was aufwendiger  ist als eine direkte Kopie eines Zahlenwertes. Daher ist es für Zahlen  (Gleitkommazahlen und Ganzzahlen) effizienter, in konstanten Variablenparameter  gehalten zu werden.

Soll ein beim Aufruf angegebenes Objekt verändert  werden, dann muß  jedoch ein nichtkonstanter Referenzparameter  eingesetzt werden.

Die konstanten Referenzparameter  haben auch ein wichtiges Anwendungsgebiet, das aber erst später behandelt werden wird.

Übungsaufgaben

Übungsaufgabe Vertauschen

Schreiben Sie eine Funktion "swap", welche die Inhalte zweier Objekte vom Datentyp "int" vertauscht.

swap.c
#include <iostream> 
#include <ostream>
// hier "swap" definieren
int main() 
{ int a( 1 ); int b( 7 ); 
swap( a, b ); std::cout << a << b << '\n'; }

std::cout
71

Hinweis Überlegen Sie für jeden Parameter, ob er ein Variablenparameter  oder ein Referenzparameter  sein sollte. Bedenken Sie weiterhin für jeden Parameter, ob er ein konstanter  oder nichtkonstanter  Referenzparameter bzw. ein konstanter  oder nichtkonstanter  Variablenparameter sein sollte.

Übungsaufgabe Zuweisung

Schreiben Sie eine Funktion "assign", welche einem Objekt einen Wert zuweist.

swap.c
#include <iostream> 
#include <ostream>
// hier "assign" definieren
int main() 
{ int a; assign( a, 7 ); std::cout << a << '\n'; }

std::cout
7

Hinweis Überlegen Sie für jeden Parameter, ob er ein Variablenparameter  oder ein Referenzparameter  sein sollte. Bedenken Sie weiterhin für jeden Parameter, ob er ein konstanter  oder nichtkonstanter  Referenzparameter bzw. ein konstanter  oder nichtkonstanter  Variablenparameter sein sollte.

Seiteninformationen und Impressum   |   Mitteilungsformular  |   "ram@zedat.fu-berlin.de" (ohne die Anführungszeichen) ist die Netzpostadresse von Stefan Ram.   |   Von der Stefan-Ram-Startseite ausgehend finden sich oft noch mehr Informationen zu Themen, die auf einer Seite angesprochen wurden. (Eine Verbindung zur Stefan-Ram-Startseite befindet sich ganz oben auf dieser Seite.)  |   Der Urheber dieses Textes ist Stefan Ram. Alle Rechte sind vorbehalten. Diese Seite ist eine Veröffentlichung von Stefan Ram. slrprd, PbclevtugFgrsnaEnz