Technisch sind beide gleich. Wenn ich anzeigen möchte, dass eine
Referenz optional ist. Also entweder auf ein gültiges Objekt zeigt, oder
aber auf nichts zeigt, dann verwendet man einen Zeiger ansonsten eine
Referenz. Eine Referenz referenziert immer ein gültiges Objekt (wenn
nicht, hat man undefiniertes Verhalten; also ein Fehler in der SW). Das
ist auch der Grund, warum ich eine Referenz immer initialisieren muss,
einen Zeiger aber nicht. Eine Referenz kann ich später auch nicht mehr
ändern. Sprich die Identität des Objekt, auf das ich referenziere bleibt
immer die selbe.
Es gibt sicher noch ein paar Spezialfälle, wo es sinnvoller ist, einen
Zeiger zu verwenden, der dann eben nicht 0 sein darf. Z.B. wenn man
einer Funktion einen Puffer übergeben möchte. Dann verwendet man in der
Regel einen Zeiger auf das erste Element eines Arrays und das sollte man
auch weiter so beibehalten, weil es einfach idiomatisch ist.
mfg Torsten
Einige Sachen funktionieren aber nur mit References. Zum Beispiel kann
man mit Pointern nur Arrays übergeben indem man auf das erste Element
zeigt. Dabei geht die Größe des Arrays verloren (array to pointer
decay). Mit References kann man tatsächlich eine Reference auf ein Array
haben (wahlweise mit der Größe als Template Parameter). Ein Unterschied
zwischen Pointer und Reference ist natürlich auch, dass man bei
References kein & (Address Operator) vor die Parameter zu schreiben
braucht. Das ist spätestens bei Operator Overloading praktisch. Ich
glaube niemand wollte Code wie
1
BigInt a, b;
2
BigInt* c = &a + &b;
schreiben. Ansonsten kann ich dem Beitrag von Torsten nur zustimmen.
mem schrieb:> Wie sieht dass denn im Speicher aus?> Wieviel Speicher belegt ein pointer und wieviel eine Referenz?
Offiziell (also laut ISO-Norm) ist eine Referenz selbst kein Objekt,
sondern nur ein anderer Name für ein bereits bestehendes Objekt und
belegt daher keinen eigenen Speicher. Deshalb kann man mit sizeof nicht
die Größe einer Referenz ermitteln und auch nicht eine mit dem operator
new dynamisch erzeugen. Auch die Adresse einer Referenz läßt sich nicht
ermitteln. Um eine Referenz zu implementieren, wird aber je nach
Anwendungsfall ein gewisser zusätzlicher Platz im Speicher benötigt.
Unter der Haube werden Referenzen dann typischerweise genau wie Zeiger
implementiert. Das ist dann aber ein Implementierungsdetail des
Compilers.
Rolf Magnus schrieb:> Offiziell (also laut ISO-Norm) ist eine Referenz selbst kein Objekt,> sondern nur ein anderer Name für ein bereits bestehendes Objekt und> belegt daher keinen eigenen Speicher. Deshalb kann man mit sizeof nicht> die Größe einer Referenz ermitteln und auch nicht eine mit dem operator> new dynamisch erzeugen.
Zur Klarstellung:
Man kann sehr wohl den Adressoperator auf eine Referenz anwenden und
bekommt die Adresse des referenzierten Objekts, analog mit sizeof.
Was du wohl meinst, ist daß man an eventuelle Hilfsdaten (heimlicher
Zeiger o.ä.) nicht herankommt.
mem schrieb:> Was erhalte ich vom Adressoperator bei einem pointer und was bei einer> Referenz?
1
int * pI;
2
3
sizeof( pI ) -> die Größe der Pointervariablen
4
sizeof( *pI ) -> die Größe dessen, worauf der Pointer zeigt. In
5
diesem Fall die Größe eines int
6
7
8
int i;
9
int& j = i;
10
11
sizeof(j) -> ist dasselbe wie sizeof(i). Die Größe eines int
Nachdem eine Referenz eingerichtet wurde, gibt es keinen UNterschied
mehr, ob du die Referenz benutzt oder direkt das originale Objekt. Eine
Referenz ist nur ein anderer Name für ein anderes Objekt. Du kannst
deine Frau ja auch 'Helga' oder 'Schatzi' nennen. Aber egal welchen
Namen du benutzt, es ist immer dieselbe Frau. So auch hier: es ist
Speicher reserviert worden, der so groß ist, dass ein int da
hineinpasst. Aber egal ob du den Variablennamen i oder den
Variablennamen j benutzt, es ist immer genau derselbe Speicherbereich
damit gemeint.
VOn daher bin ich mit der Aussage, dass Referenzen und Pointer technisch
identisch sind eher unglücklich. Denn das sind sie nicht. Die Semantik
von Referenzen wird oft mit einem Pointer im Hintergrund realisiert,
aber das deckt nicht alle Fälle ab und ist für das Verständnis von
Referenzen nicht hilfreich. Es ist ein Implementierungsdetail, denn
irgendwie müssen Referenzen ja tatsächlich realisiert werden und das
geht in manchen Fällen nicht anders. Aber es ist nur die halbe Wahrheit.
Im obigen Beispiel wird der Compiler zb für die realisierung von j eben
keinen Pointer benutzen, sondern sich in seinen Tabellen eintragen, dass
eigentlich i gemeint ist, wann immer du j benutzt (bzw. das j denselben
Speicherbereich kennzeichnet wie i). Eben ein anderer Name für dasselbe
Objekt (= denselben Speicherbereich)
sizeof ist zwar nicht der Adressoperator, aber du hast die Lunte
gerochen. ;-)
Eine Referenz ist nur ein anderer Name. Und ein pointer ein eigener
Datentyp. Für mich sind das zwei völlig verschiedene Dinge, auch wenn
sie nach aussen gleich erscheinen.
Ein array ist ja auch kein pointer. Aber das ist eine gaaannnzzz andere
Sache. ;-)
Frohes Fest!
Karl Heinz schrieb:> VOn daher bin ich mit der Aussage, dass Referenzen und Pointer technisch> identisch sind eher unglücklich.
Inhaltlich aber ist das nicht ganz falsch, wenn so etwas an eine
Funktion übergeben wird. Aber auch nicht komplett synonym ...
1
voidblafusel(int&x)
2
{
3
x=4;
4
}
5
6
voidmachwas(void)
7
{
8
inti=0;
9
10
blafusel(i);
11
12
printf("%d\n",i);
13
}
Hier zeigt sich auch etwas, was ich als Nachteil, oder zumindes sehr
gewöhnungsbedürftige Eigenschaft von Referenzen ansehe.
In C (das keine Referenzen kennt) kann man bei alleiniger Betrachtung
der Funktion "machwas" klar sehen, daß "i" als 0 ausgegeben werden wird,
da der Funktionsaufruf von "blafusel" die Variable nicht verändern
kann.
In C genügt es dafür, den Aufruf einer Funktion zu sehen, ohne sich
die Funktion selbst oder deren Prototyp anzusehen.
In C++ (mit Referenzen) genügt das nicht; obiges Beispiel zeigt das.
Somit ist C++-Code definitiv anders (und mit mehr Vorsicht) zu lesen als
C-Code, weil etwas, was in C ein Objekt nicht verändern kann, in C++ das
sehr wohl kann.
(Ob es das letztlich tut, ist wieder eine ganz andere Angelegenheit)
Das ist aber in Pascal-artigen Sprachen auch nicht anders, dort lässt
sich bei alleiniger Betrachtung des Aufrufs einer Funktion (oder
Prozedur) nicht erkennen, ob dort "call by value" oder "call by
reference" durchgeführt wird. Um das zu erkennen, muss man die
Funktions/Prozedurdefinition ansehen.
Klar, wenn man eigenen Code schreibt, ist das kein relevantes Problem,
aber wenn man fremden Code durchliest, dann ist so etwas nicht ganz
bedeutungslos.
Rufus Τ. Firefly schrieb:> In C (das keine Referenzen kennt) kann man bei alleiniger> Betrachtung der Funktion "machwas" klar sehen, daß "i" als 0> ausgegeben werden wird, da der Funktionsaufruf von "blafusel" die> Variable nicht verändern kann.
Es sei denn, "blafusel" ist ein Makro oder i ist en Array. Das tolle an
C ist, daß es bei allen Regeln Ausnahmen gibt.
Generell ist es keine gute Idee, Funktionen aufzurufen, ohne zu wissen,
was sie mit den Parametern machen, egal ob in C oder C++. Da gibt es
noch viel mehr Stolperfallen, als eine den übergebnen Wert ändernde
Funktion.
Es ist halt nur anfangs ungewohnt, daß das in C++ mit Referenzen möglich
ist.
Rolf Magnus schrieb:> Es sei denn, "blafusel" ist ein Makro oder i ist en Array.
Welchen Typ i hat, sieht man in diesem Fall, da es hier in der Nähe des
Aufrufs von "blafusel" definiert wird.
"blafusel" sollte kein Makro sein, sofern der Programmierer sich an
die Konventionen gehalten hat -- Makronamen werden in VERSALIEN
geschrieben.
Allerdings ist das bei fremden Quelltexten keine Annahme, von der man
unbedingt ausgehen kann.
Insofern hast Du natürlich recht.
In manchen Coderichtlinien (u.a. Google C++ Style Guide) wird deshalb
empfohlen, Parameter an Funktionen nur per Value, per Const-Referenz
oder per (Non-Const-)Zeiger zu übergeben.
Wenn man sich daran hält, ist beim Aufruf auf einen Blick ersichtlich,
was als Ein- und Ausgabeparameter gedacht ist.
Ich nutzte außerdem Zeiger statt Referenzen, wenn sich die Funktion die
Adresse über den Funktionsaufruf hinweg merkt. Pass-by-Const-Reference
ist dann einfach eine effizientere Form von Pass-by-Value, die sich aber
semantisch und syntaktisch gleich verhält.
> Pass-by-Const-Reference ist dann einfach eine effizientere Form von> Pass-by-Value
Aber auch nur bei größeren Objekten ("größer" ist hier natürlich relativ
und von der Umgebung abhängig). Ich habe schon "const long&" u.ä. als
Parameter gesehen ...
> die sich aber semantisch und syntaktisch gleich verhält.
void hmmm(int x)
{
x = 42;
}
void hmmm(const int& x)
{
const_cast<int&>(x) = 42;
}
Willi Wampe schrieb:>> die sich aber semantisch und syntaktisch gleich verhält.>> void hmmm(int x)> {> x = 42;> }>> void hmmm(const int& x)> {> const_cast<int&>(x) = 42;> }
Und was willst du damit sagen, außer daß const_cast immer im
Zusammenhang mit einem Programmierfehler auftaucht?
Hans schrieb:> In manchen Coderichtlinien (u.a. Google C++ Style Guide) wird> deshalb> empfohlen, Parameter an Funktionen nur per Value, per Const-Referenz> oder per (Non-Const-)Zeiger zu übergeben.>> Wenn man sich daran hält, ist beim Aufruf auf einen Blick ersichtlich,> was als Ein- und Ausgabeparameter gedacht ist.
In komplexeren Beispielen ist dies aber meist schon sehr schnell nicht
mehr anwendbar:
1
structresult{...};
2
3
voidadd_bar(result*);
4
voidadd_foo(result*);
5
6
voidfill_in_root_cause(result*r)
7
{
8
assert(r);
9
10
add_bar(r);
11
add_foo(r);
12
}
13
14
intmain()
15
{
16
resultr;
17
...
18
fill_in_root_cause(&r);
19
}
hier ist jetzt nur noch an einer von drei Stellen sichtbar, dass der
Parameter geändert wird. Und das Beispiel ist jetzt auch nicht wirklich
besonders konstruiert. Wenn Du z.B. so etwas wie eine Senke für ein
Protokoll hast, dann wird diese Senke sicher über mehrere Ebenen weiter
gereicht. Zusätzlich handelt man sich die Möglichkeit ein, dass der
übergebene Parameter 0 sein kann.
Die einzige, wirklich funktionierende Vorgehensweise, ist meiner Meinung
nach vernünftige Namen für Variablen und Funktionen zu verwenden.
mfg Torsten
N'abend,
browse gerade durch die Threads und kann mich nicht zurück halten...
1. Cool bei Referenzen ist die Funktionsübergabe von fixed sized arrays
(compile time checked) also etwas wie "int (&array)[100]". Das geht
nicht in C. ...
2. Schön bei Referenzen als Parameter ist der implizite Ausschlus von
NULL Pointern (wenn man nicht gerade castet)...
3. Const References als Funktionsparameter sind ok (im Sinn von Punkt
2)...
4. Non-Const References als Funktionsparameter oder Rückgabewerte sind
verwirrend, wenn man nur die Caller-Side sieht: Soll heißen, man sieht
nicht ob das ein Value oder eine "Reference" ist und weiß somit nicht ob
bei Modifikationen das Urpsungsobjekt oder eine Kopie verändert wird...
(ich habe eine Zeitlang statt dessen Pointer benutzt, ist aber auch
keine optimale Lösung)...
5. Eine Reference als Object Variable ist interessant... (don't do it
unless you know what you do).
Grüße
btw: Ich mag Karl Heinz' Beiträge!
Sebastian V. O. schrieb:> Einige Sachen funktionieren aber nur mit References. Zum Beispiel kann> man mit Pointern nur Arrays übergeben indem man auf das erste Element> zeigt. Dabei geht die Größe des Arrays verloren (array to pointer> decay). Mit References kann man tatsächlich eine Reference auf ein Array> habenN.R. schrieb:> 1. Cool bei Referenzen ist die Funktionsübergabe von fixed sized arrays> (compile time checked) also etwas wie "int (&array)[100]". Das geht> nicht in C. ...
Man kann doch in C auch einen Zeiger auf ein Array übergeben (nicht nur
einen auf dessen erstes Element). Man muss diesen Zeiger halt an den
Stellen, wo man das Array benutzen möchte, mit dem *-Operator
dereferenzieren. Das ist aber immer so, wenn man C++Referenzen durch
Zeiger nachbildet.
Eine Referenz in C++ ist im Wesentlichen ein selbstdereferenzierender
Zeiger mit ein paar durch diese automatische Dereferenzierung bedingten
Einschränkungen (z.B. keine Zuweisung möglich, deswegen Initialisierung
immer erforderlich). Man kann praktisch alles, was mit Referenzen
möglich ist, mit gewöhnlichen Zeigern nachbilden, allerdings entsteht
dabei etwas mehr Schreibarbeit, da man häufiger die *- und &-Operatoren
bemühen muss.
Yalu X. schrieb:> Man kann doch in C auch einen Zeiger auf ein Array übergeben (nicht nur> einen auf dessen erstes Element).
Stimmt, da hast du recht.
Mir ist gerade aber noch eine andere Eigenheit von References in C++
eingefallen. Wenn man const References als Parameter von Funktionen hat,
dann verhält es sich ziemlich genau wie wenn ich direkt den Wert
übergeben hätte. Insbesondere ist auch sowas erlaubt:
1
void foo(const MyClass& x) { ... }
2
...
3
foo(MyClass());
Also einen temporären Wert übergeben. Der C++ Standard garantiert hier,
dass der Parameter bis nach dem Funktionsaufruf erhalten bleiben. Dies
gilt aber nur für const References! Bei einer normalen Reference wird
das temporäre Objekt in der Regel vorm Funktionsaufruf wieder zerstört.
Visual Studio hat hier aber wieder seine Eigenheiten und eine nicht
standardisierte Erweiterung die auch nicht const References erlaubt.
Rufus Τ. Firefly schrieb:> Makronamen werden in VERSALIEN> geschrieben.
Die intention ist schön, wird aber schon in der Standard-C-Bibliothek
nicht eingehalten...
N.R. schrieb:> Cool bei Referenzen ist die Funktionsübergabe von fixed sized arrays
Cool ist auch, dass man in c++ einen vector verwenden kann. Der hat eine
Größeninformation und braucht kaum mehr Speicher.
Sebastian V. O. schrieb:> Bei einer normalen Reference wird> das temporäre Objekt in der Regel vorm Funktionsaufruf wieder zerstört.
An eine normale Reference darfst du mit einem gescheiten Compiler
garkeine temporären Werte übergeben. Da sollte wenigstens eine Warnung
rausspringen.
Eine Referenz ist ungefähr so viel Zeiger, wie ein Hardlink eine
symbolische Verknüpfung ist...