Rückgabe von Exemplaren in C++
Wie schon im Grundkurs an Hand von »::std::string« gezeigt, kann der Rückgabetyp einer Funktion auch ein benutzerdefinierter Typ sein. Im allgemeinen muß der return-Ausdruck dann dazu passen.
Wie schon bei der Argumentaufgabe, so erfolgt auch hier die Festlegung eines Wertes mit »return« erfolgt wie bei einer Kopierinitialisierung (copy-initialization ). Eine eventuell nötig Typwandlung darf nicht explizit sein.
Syntax
- return [<expr-or-braced-init-list>];
main.c
#include <initializer_list>
#include <ostream>
#include <iostream>
#include <string>using namespace ::std::literals;
::std::string f(){ return "abc"s; }
::std::string g(){ return "abc"; }
::std::string h(){ return { 65, 66 }; }int main()
{ ::std::cout << f() << '\n'; /* schreibt "abc" */
::std::cout << g() << '\n'; /* schreibt "abc" */
::std::cout << h() << '\n'; /* schreibt "AB" */ }
Durch gewisse Tricks, kann eine moderne C++-Implementation es in der Regel vermeiden, daß größere Objekte bei der Rückgabe zeitaufwendig kopiert werden müssen. Deswegen ist heute nicht mehr verpönt, auch größere Objekt direkt zurückzugeben.
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>
#include <initializer_list>
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.
Referenzen auf lokale Variablen in Rückgaben
Man muß daran denken, daß die Existenz einer lokalen Variablen mit der Existenz der Inkarnation der sie enthaltenden Funktion endet. Daher wäre die Rückgabe einer Referenz auf eine lokale Variablen in der Regel eine Rückgabe einer Referenz auf ein nicht mehr existentes Objekt. Solch eine Rückgabe sollte daher vermieden werden.
Rückgabe von Referenzen in sinnvoller Weise
»::std::cout« kann nicht kopiert werden, und die Rückgabe erlaubt bequeme Verwendungen.
main.cpp
#include <iostream>
#include <ostream>
#include <string>
#include <memory>
using namespace ::std::literals;
::std::ostream & put_example( ::std::ostream & stream )
{ return stream << "example"s; }
int main(){ put_example( ::std::cout ).put( '\n' ); }::std::cout
example