Referenzen und Zeiger
-
Hallo,
ja, mit Speicherbereich kann mich schon sehr viel schneller anfreunden. Warum sind denn Referenzen syntaktischer einfacher zu handhaben? Die Adresse eines Zeigers ist doch auch fast immer ein Objekt?Vielen Dank für die rasche Antwort.
lg, freakC++
-
freakC++ schrieb:
1.) Eine Referenz muss ja immer bei ihrer Definition initialisiert werden. Sie ist ein Aliasname einer anderen Variable. Wie funktioniert das denn. Sie muss doch eigentlich auch die Adresse einer anderen Variablen speichern. Was ist dann der prägnante Unterschied zu Zeigern?
Eine Referenz ist eigentlich eine Abstraktion eines Zeigers. Sie ist lediglich ein Verweis auf ein Objekt; Dinge wie Zeigerarithmetik, ungültige Werte (Null) oder Adressen sind erstmal irrelevant. Intern sind sie oft wie Zeiger implementiert, allerdings muss das nicht sein und es besteht normalerweise kein Anlass, dies zu erfahren.
freakC++ schrieb:
2.) Eine Referenz ist der Aliasname einer anderen Variable. Wenn ich nun folgendes schreibe:
const double& pi = 3.1415927;wo liegt dann der Bezug auf einer anderen Variable? Hier liegt doch kein anderes Objkekt vor? Es heißt ja nicht:
const double pi = 3.1415927; const double& rpi = pi;Referenzen auf
constwerden in dieser Hinsicht speziell behandelt. Sie können auf temporäre Objekte zeigen und deren Lebensdauer über den Ausdruck, nach dem diese eigentlich zerstört würden, hinweg verlängern.SeppJ schrieb:
Schreib statt Variable lieber 'Speicherbereich'. Damit dürften sich auch deine Fragen zu const Referenzen erledigt haben.
Wie angetönt finde ich die Assoziation mit Speicherbereichen bei Referenzen (im Gegensatz zu Zeigern) nicht allzu sinnvoll. Ausserdem frage ich mich, wie dies die Behandlung von Const-Referenzen gegenüber Non-Const-Referenzen erklären soll?
freakC++ schrieb:
Warum sind denn Referenzen syntaktischer einfacher zu handhaben?
Weil die explizite Dereferenzierung entfällt.
freakC++ schrieb:
Die Adresse eines Zeigers ist doch auch fast immer ein Objekt?
Nein, zumindest nicht Objekt im C++-Sinne. Was meinst du?
-
Referenzen sind einfacher zu handhaben weil man sie nicht dereferenzieren muss um auf das Objekt zugreifen zu können.
Dein beispiel mit dem const ist ein Spezialfall: eine konstante Referenz kann an ein temporäres Objekt gebunden werden (in den meisten Fällen passiert das halbwegs ungesehn bei Funktionsparametern). Die Lebenszeit des temporären Objektes verlängert sich dann auf den Gültigkeitsbereich der Referenz.
Siehe auch hier für mehr: http://herbsutter.wordpress.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
-
Eine initialisierte Referenz ist das Objekt:
foo a = ... ; foo& b = a; // ... irgendwas ...es ist hier völlig unmöglich festzustellen, ob a oder b die originale variable und das andere die Referenz ist:
foo b = ...; foo& a = b; // ... irgendwas ...ist das gleiche Programm. Man kann sich auch einfach vorstellen, dass normale Variablen einfach Referenzen sind, mit dem Zusatz, dass deren Definition die Erzeugung des jeweiligen Objektes impliziert.
Im Gegensatz dazu besteht ein klarer und offensichtlicher Unterschied zwischen Objekten und Zeigern auf diese Objekte.
-
Hallo,
vielen Dank für die Antworten. Ich habe mir mal den Inhalt der Seite, die pumuckl mir vorgeschlagen hat, durchgelesen. Dazu habe ich eine Frage. Ich habe diesen Code:string f() { return "abc"; } //Funktion gibt ein temporäres Objekt zurück void g() { string& s = f(); //nicht korrekt cout << s << endl; }Folgende Fragen habe ich dazu:
1.) Warum gibt die Funktion ein temporäres Objekt zurück. Eigentlich ist nur der return temporör, weil alles was mit der Funktion zu tun hat nach dem Aufruf wieder gelöscht. Da aber der zurückgegebene Wert irgendwo gespeichert wird, ist er doch gar nict richtig temporär?!
2.) Meine Referenz soll nun den return-Wert von f() speichern. Wo liegt das Problem? Ich konnte der Erklärung der Seite nicht ganz folgen, da ich nicht verstehe, warum das const so essentiel ist.
3.) Es ist aber richtig zu sagen, dass eine Referenz auch die Adresse einer Variablen hat? Wie soll das sonst funzen?
4.) Diesen Code verstehe ich leider überhaupt nicht.
string& message() { static string str = "Hallo"; return str; } int main() { message() = "Tschüss"; string s=message(); cout << s << endl; }Seit wann kann ich einer Funktion Werte zuweisen. Kann mir da mal jemand auf die Sprünge helfen, da dass ja an dem Rückgabewert als Referenz liegen muss.
Vielen Dank
lg, freakC++
-
1. Der string, den du da zurück gibst, ist temporär. Er hat ja keinen Namen und ist nur in dem aktuellen Kontext gültig. Wenn du da jetzt eine Referenz drauf speicherst, ist er nach dem nächsten Sequenzpunkt ( z.B ; ) nicht mehr gültig.
2. const ist da nötig, damit der Compiler weiss, dass du den Wert nicht änderst und somit das reine lesen erlaubt ist. (steht im Standard irgendwo; schaue vlt. später nach, wo genau).
3. Die Adresse einer Referenz ist die Adresse des referenzierten Objektes. Stell dir die Referenz eher als Alternativ Namen vor, als ein Zeiger. Sprich zur Laufzeit hast du kein "Referenz"-Objekt, wie du z.B einen Zeiger speichern kannst.
4. Der Punkt liegt hier bei dem static. Ein statisches Objekt wird beim erreichen des Codes erzeugt und erst am Ende des gesamten Programmes wieder zerstört. Daher kannst du auch Problemlos darauf verweisen.
Das du dem Funktionsaufruf einen Wert zuweisen kannst hat den Grund, dass die Funktion ja den Rückgabewert einer Referenz auf den string hat. Und einer Referenz kannst du ja einen Wert zu weisen. Du weisst also nicht der Funktion, sondern dem Rückgabewert etwas zu. (dass das geht, wir oft als Grund genannt, einen Rückgabewert const zu machen - um z.B den ==/= Fehler zu umgehen).
-
eine noch schönere Variante ist folgendes:
NEVER DO THIS! NIEMALS MACHEN!
std::string& genMessage(uint32 i) { static std::string abc; /* mach was mit dem Input und beschreibe den String */ return abc; }NEVER DO THIS! NIEMALS MACHEN!
Das hat ganz böse nebenwirkungen bei multithreading.
Sowas sieht man hin und wieder bei Anfängern, die gerade erfahren haben, dass bei Rückgabewerten temporäre Objekte erstellt werden, die sie so eingespart werden sollen.
-
freakC++ schrieb:
wo liegt dann der Bezug auf einer anderen Variable?
Muss nicht, dass nennt man call-by-value.
-
eine referenz kann man sich am besten als selbstreferenzierenden zeiger
vorstellen.gehen wir von deinem beispielscode aus:
string& message() { static string str = "Hallo"; return str; } int main() { message() = "Tschüss"; string s=message(); cout << s << endl; }jetzt stell dir vor, die referenzen wären zeiger (zeig mir einen realen
compiler, der das so nicht handhabt), und bei jedem zugriff bekämen wir
unser objekt (per * dereferenziert):string *message() // die referenz in form einer addresse { static string str = "Hallo"; return &str; // referenz, also addresse zurückgeben } int main() { *message() = "Tschüss"; // addresse holen, dereferenzieren und reinschreiben string s = *message(); cout << s << endl; }die addresse einer referenz bekommst du nicht, da sie für dein objekt steht.
kann man sich auch herleiten:int i; int &r = i; int *ptr = &r; // wird zu int i; int *r = &i; int *ptr = &*r; // das kürzt sich!!ich wette mit dir, dass beide versionen auf assemblerebene gleich aussehen.
die größen unterschiede liegen in der syntax. so brauchst du eine referenz
nicht zu dereferenzieren (auch das kann man aus dem zeigerbeispiel erkennen).auf null setzen geht auch nicht.
(mit einem trick schon: int &r = *(int *)0; jeder zugriff würde aber
abschmieren).so.
das ganze war eine veranschaulichung zur besseren verständniss. nichts davon
ist wirklich durch den c++ standard vorgeschrieben.(offtopic: ist die zahl der bits in einem byte per standard festgelegt??)
in den meisten fällen kannst du einen zeiger durch eine referenz ersetzen,
andersrum (referenz durch zeiger) geht das immer (außer bei diesem const-teil...)wenn du z.b. deine referenzen/zeiger oft umsetzt oder als ungültig markierst (0),
ist ein zeiger die bessere wahl. willst du ein objekt wie ein anderes
behandeln (proxy-klassen) oder die schöne streaming syntax nutzen (cout <<),
sind referenzen besser.
wenn du einen konstanten parameter hast, der nicht kopiert werden soll, überzeugt
eine referenz schon aus syntaktischen gründen.dynamische speicherverwaltung (new/delete) ist aber wieder ein klarer fall für
zeiger. kein mensch schreibt sowas:obj &o = *new obj; delete &o;
-
Hey,
vielen Dank für die ausführlichen Erklärungen. Ich denke, dass ich es verstanden habe. Falls dennoch Fragen wieder aufkommen sollte, dann werde ich mich melden!Bis dann
lg, freakC++
-
Hallo zusammen,
da bin ich nochmals. Ich habe nochmal eine Frage zu diesem Aufruf:int& max(int x, int y) { return x > y ? x : y; }Warum wird hier der Compiler eine Fehlermeldung ausgeben. Kann man überhaupt noch einen Rückgabetyp einer Funktion vom Typ "Referenz auf Typ" machen? Könnt ihr mir das nochmals an einem guten Beispiel erläutern?
Vielen Dank
lg, freakC++
-
freakC++ schrieb:
int& max(int x, int y) { return x > y ? x : y; }Das klappt nicht, weil x eine lokale Variable innerhalb der Funktion ist und totgeht, wenn die Funktion verlassen wird. Eine referenz auf was totes ist nicht gut.
int& max(int& x, int& y) { return x > y ? x : y; }So wäre erzwungen, daß der Aufrufer Referenzen auf außen lebende Variablen reinsteckt und eine Referenz auf eine dieser außen lebenden Variablen zurückzugeben, istr ok.
-
Hallo,
das leuchtet mir zwar ein, doch dann verstehe ich dieses Beispiel nicht
:string& message() { static string str = "Hallo"; return str; } int main() { string str = message(); cout << str << endl; return 0; }Dieses Beispiel hatten wir zwar schonmal, aber egal. Wenn ich nun das static wegmache, wird str auch zu einer lokalen Variable. Wenn man mal vom Logischen absieht: Das Programm wird auch ohne static kompiliert. Warum kompiliert dann das andere Beispiel nicht. Da sind doch auch "nur" lokale Variablen?!
Vielen Dank
lg, freakC++
-
Grundsätzlich kompiliert auch das obige Beispiel. Benenn es einfach mal um.

Das liegt daran, dass max ein Makro ist und dann bekommt der Compiler da ein sehr merkwürdiges Konstrukt vorgelegt.

-
freakC++ schrieb:
Hallo zusammen,
da bin ich nochmals. Ich habe nochmal eine Frage zu diesem Aufruf:int& max(int x, int y) { return x > y ? x : y; }Warum wird hier der Compiler eine Fehlermeldung ausgeben.
Warum sollte er?
-
camper schrieb:
freakC++ schrieb:
Hallo zusammen,
da bin ich nochmals. Ich habe nochmal eine Frage zu diesem Aufruf:int& max(int x, int y) { return x > y ? x : y; }Warum wird hier der Compiler eine Fehlermeldung ausgeben.
Warum sollte er?
neben dem von drakon vermuteten fakt, dass es ein max-makro gibt, gibts auch noch die möglichkeit, dass er algorithm included hat und using namespace std geschrieben hat - dann wird vermutlich kommen, dass man fkt. nicht mittels rückgabe-typ überladen kann...
bb
-
unskilled schrieb:
neben dem von drakon vermuteten fakt, dass es ein max-makro gibt
achso, dafür gibts NOMINMAX.
unskilled schrieb:
gibts auch noch die möglichkeit, dass er algorithm included hat und using namespace std geschrieben hat - dann wird vermutlich kommen, dass man fkt. nicht mittels rückgabe-typ überladen kann...
Stimmt, allerdings stehen in std nur zwei Funktionstemplates für max, kein Problem also.
-
camper schrieb:
unskilled schrieb:
neben dem von drakon vermuteten fakt, dass es ein max-makro gibt
achso, dafür gibts NOMINMAX.
im nachhinen gibts dafür nur noch #undef min bzw #undef max, da jeder, der die windows-header included, NOMINMAX davor definieren müsste - machen aber die wenigsten (libs)... würd ich mich auch nie drauf verlassen...
camper schrieb:
unskilled schrieb:
gibts auch noch die möglichkeit, dass er algorithm included hat und using namespace std geschrieben hat - dann wird vermutlich kommen, dass man fkt. nicht mittels rückgabe-typ überladen kann...
Stimmt, allerdings stehen in std nur zwei Funktionstemplates für max, kein Problem also.
stimmt ^^
bb
-
Hallo zusammen,
jetzt hab' ich es begriffen. Der Code kompiliert nun auch.Bis bald
lg, freakC++