Zeiger oder Referenz?
-
Hallo zusammen!
bin grad wieder fleißig am Programmieren (totaler anfänger!) und komm mit zeigern und referenzen nicht ganz zurecht. Kann mir jemand sagen, wann ich zeiger und wann referenzen verwenden sollte? und gibt es fälle, in denen ich nur zeiger und keine referenzen (oder umgekehrt) verwenden kann? Hoffe es kann mir jemand weiterhelfen. Danke schonmal!
-
1. Zeiger sind mächtiger wie Referenzen
2. Mann sollte im allgemeinen Referenzen verwenden statt Zeiger, wenn es möglich ist
-
wenn ich eine Variable mit einem anderen Wert überschreiben möchte, kann ich das auch mit referenzen machen?
-
gibt es fälle, in denen ich nur zeiger und keine referenzen (oder umgekehrt) verwenden kann?
wenn du speicher mit new reservierst brauchst du einen Zeiger
wenn ich eine Variable mit einem anderen Wert überschreiben möchte, kann ich das auch mit referenzen machen?
include <iostream>
using namespace std;void addition(int& summe, const int& operant1, const int& operant2)
{summe = operant1 + operant2;
}
int main()
{
int a = 4;
int b = 6;
int ergebnis;
addition(ergebnis, a, b);
}Tutorial zu Zeiger + Referenzen:
http://www.zfx.info/Tutorials.php?ID=96
-
Es gibt ja zwei Zugriffsmodi in Funktionen:
Call by reference und Call by value
Call by value bedeutet, ich rufe die Funktion mit einem Wert auf, der in der Funktion verarbeitet wird, jedoch ändert sich nichts an dem Wert, den ich übergebe.
Call by reference ist entweder mit einer Referenz (wie man am Namen schon hört), oder mit einem Pointer. Bei einer Referenz wird der Inhalt mit verändert, jedoch muss man beispielsweise, wie im Vergleich mit einem Pointer nicht dereferenzieren, was für manche den Umgang damit erleichtert, jedoch kann man mit Zeigern beispielsweise auch die Adresse der behandelten Variable verändern, oder die Adresse, auf die gezeigt wird und Referenzen sind in diesem Fall fest. Bei Call by reference ändert sich der Wert der übergebenen Variable, wodurch man beispielsweise return-Werte umgehen kann, oder dafür sorgen, dass mehrere Werte durch eine Funktion verändert werden
-
ich habe einen array, der im laufe des Programms seinen ihalt wechselt, nicht aber seine Größe. wie mache ich das mit referenzen?
Beispiel:
erster Schleifendurchlauf:
a[0]=5;
a[1]=6;
a[2]=8;im nächsten Durchlauf soll das array dann andere Werte beinhalten:
a[0]=12;
a[1]=34;
a[2]=88;
-
Du könntest die Speicherstelle als Referenz übergeben..
void change(int& wandel_mich) { wandel_mich += 2; } int main() { int arr[4]; for(int i = 0; i < 4; ++i) arr[i] = i+(i*2); for( i = 0; i < 4; ++i) change(arr[0]); return 0; }
-
Vertexwahn schrieb:
1. Zeiger sind mächtiger wie Referenzen
2. Mann sollte im allgemeinen Referenzen verwenden statt Zeiger, wenn es möglich istunfug. denk erstmal nach, bevor du strcpy mit referenzparametern ausrüstetst.
-
Vertexwahn schrieb:
wenn du speicher mit new reservierst brauchst du einen Zeiger
Nicht zwängsläufig. Du kannst auch direkt dereferenzieren:
foo& a = *new foo; //... delete &a;Ist zwar keine empfehlenswerte Vorgehensweise (imo), aber möglich. Das ist übrigens legaler Code, da new keinen Nullzeiger zurückgibt.
-
volkard schrieb:
Vertexwahn schrieb:
1. Zeiger sind mächtiger wie Referenzen
2. Mann sollte im allgemeinen Referenzen verwenden statt Zeiger, wenn es möglich istunfug. denk erstmal nach, bevor du strcpy mit referenzparametern ausrüstetst.
Es gibt halt einige C Programmierer, die denken Referenzen sind unnötig, da man ja mit Zeigern das gleiche erreicht - dahher habe ich das so "hart" formuliert
Nicht zwängsläufig. Du kannst auch direkt dereferenzieren:
foo& a = *new foo; //... delete &a;ihr seid doch alle krank

-
Vertexwahn schrieb:
Es gibt halt einige C Programmierer, die denken Referenzen sind unnötig, da man ja mit Zeigern das gleiche erreicht - dahher habe ich das so "hart" formuliert
hart formulieren ist ok. aber lügen ist nicht ok.
diesen c-programmierern sagte einfach, daß sie ohne referenzen keinen copy-ctor bauen können. und viele operatoren werden erst mit referenzen erlauben, daß sie die klasse wie ein int anfühlt.
-
Letztendlich sind Referenzen imo trotzdem nur eine Vereinfachung von Pointern:
- Man braucht die Syntax mit dem Dereferenzierungsoperator nicht wenn man einen Übergabeparameter verändern will,
- Referenzen können `eigentlich` nicht ungültig sein und
- man spart sich wie bei Pointern den Cpy C'tor.Dem Compiler wirds wohl im Endeffekt egal sein...
-
FireFlow schrieb:
- man spart sich wie bei Pointern den Cpy C'tor.
verstehe ich nicht - warum spart man sich den?
-
Ich sehe die groessere Sicherheit mit Referenzen schon, aber ich persoenlich habe immer mit Zeigern gearbeitet, da eine Referenz in der letzten Instanz doch nur "an alternative name for an object" ist. Ich persoenlich arbeite dann lieber mit einem Pointer auf das Object, den ich auch NULL setzen kann, falls dieser nicht gesetzt sein sollte oder konnte.
Den tieferen Sinn des Copy-Konstruktors habe ich auch nie eingesehen - zumindest nicht bei nicht-trivialen Klassen. Wenn zwei oder mehr Instanzen einer Klasse benotige, die die gleichen Daten enthalten oder sich nur unwesentlich unterscheiden, dann baue ich eher eine weitere Klasse, die die gemeinsamen Daten beinhalten und lasse einen Pointer als Member der Klasse auf die Klasse mit den gemeinsamen Daten zeigen.
Aber das ist keine dogmatische Erklaerung, das ist mein Stil C++ zu schreiben.
-
Vertexwahn schrieb:
FireFlow schrieb:
- man spart sich wie bei Pointern den Cpy C'tor.
verstehe ich nicht - warum spart man sich den?
Naja bei Built-In Types ist es ja kein Problem, da soll es sogar schneller sein wenn by-value übergibt. Ist doch klar dass wenn man eine Referenz auf eine Klasse übergibt da kein CopyConstructor aufgerufen werden muss weil gar nix kopiert wird. Bei by-value wird ja eine lokale Variable erstellt. Bei einer Container-Klasse ist das weniger gut

Gruß
-
@fireflow
das kannst Du mit zeigern genauso mache. Ein Copy-Ctor ist da um etwas zu kopieren. Wenn du kopieren willst/musst, hilft Dir ne Referenz nicht, d.h. die Aussage, dass man sich mit Referenzen den Copy-Ctor spart ist falsch oder zumindest sehr missverständlich.Den tieferen Sinn des Copy-Konstruktors habe ich auch nie eingesehen - zumindest nicht bei nicht-trivialen Klassen. Wenn zwei oder mehr Instanzen einer Klasse benotige, die die gleichen Daten enthalten oder sich nur unwesentlich unterscheiden, dann baue ich eher eine weitere Klasse, die die gemeinsamen Daten beinhalten und lasse einen Pointer als Member der Klasse auf die Klasse mit den gemeinsamen Daten zeigen.
Es ist schon ein semantsicher Unterschied, ob Du zwei Objekte hast, oder ein Objekt mit zwei Referenzen darauf. Wenn Du ein Backup machst, ziehst Du auch nicht einfach ne Verknüpfung auf die CD. Du brauchst ne Möglichkeit einen String zu kopieren. Ok, in Java gibts da dann halt ne spezielle Kopier-Methode. Aber mit dem Copy-Ctor kannst Du's halt so machen, dass es sich bei allen Typen gleich 'natürlich' anfühlt, wie bei nem integer.
Ich persoenlich arbeite dann lieber mit einem Pointer auf das Object, den ich auch NULL setzen kann, falls dieser nicht gesetzt sein sollte oder konnte.
Dafür nehm ich zwischenzeitlich boost::optional. Zeiger da, wo sich das referenzierte Objekt ändern kann (z.B. pFreundin
), es aber immer eines gibt (also doch nicht pFreundin
).In Verbindung mit dynamischen Objekten nimmt man am besten keine nackten Zeiger, sondern smart-Pointer.
Also:
Das zu referenzierende Objekt ändert sich nicht -> Referenz
Das zu referenzierende Objekt kann sich ändern ist aber niemals unbelegt -> Zeiger
Das zu referenzierende Objekt kann unbelegt sein -> boost::optional
Es handelt sich um ein dynamisches Objekt, Gültigkeitsdauer und Besitzverhältnisse sind wichig -> entsprechenden smart-Pointer.
-
So am Rande: wem der Sinn von Referenzen nicht schlüssig ist sollte sich mal etwas Zeit nehmen und nen richtiges C++ Buch durchlesen.
-
kartoffelsack schrieb:
Den tieferen Sinn des Copy-Konstruktors habe ich auch nie eingesehen - zumindest nicht bei nicht-trivialen Klassen. Wenn zwei oder mehr Instanzen einer Klasse benotige, die die gleichen Daten enthalten oder sich nur unwesentlich unterscheiden, dann baue ich eher eine weitere Klasse, die die gemeinsamen Daten beinhalten und lasse einen Pointer als Member der Klasse auf die Klasse mit den gemeinsamen Daten zeigen.
Es ist schon ein semantsicher Unterschied, ob Du zwei Objekte hast, oder ein Objekt mit zwei Referenzen darauf.
Vielleicht war nicht ganz klar. Mal folgender Fall - eine einfache Bankkontenverwaltung:
class EinzelKonto { private: float iKontostand; //in Geldeinheiten int iKontoNr; //und noch mehr Daten zum Konto //Ab hier Daten zur Kontoart (Sparbuch, Giro, anlage ...) char *Bezeichnung; float fZinsatz; int iKuendigsfrirst; //und noch viel mehr //und hier div. Methoden .. }In jeden Object der Klasse Einzelkonto wiederholen sich Daten, die schon sehr viele Male verwendet wurde - damit wird jede Menge Heap verschwendet. In diesem Falle wuerde ich folgende Loesung machen:
class EinzelKonto { private: float iKontostand; //in Geldeinheiten int iKontoNr; //und noch mehr Daten zum Konto KontoTyp *pKontoTyp; } class KontoTyp { char *Bezeichnung; float fZinsatz; int iKuendigsfrirst; //und noch viel mehr //und hier div. Methoden .. }Sollte ein Konto keinem Typ zuzuordnen sein, so wuerde ein NULL-Pointer erscheinen. Das erspart jede Menge Speicher.
-
In Deinem Beispiel ist es schon schön und gut einen Zeiger zu verwenden (obwohl hier aus semantischer Sicht etwas wie boost::optional eigentlich schöner ist, weil NULL letzenendes auch nur irgendeine Magic-Number ist - aber immerhin eine so weit verbreitete, dass es nicht viel dagegen einzuwenden gibt. http://www.boost.org/libs/optional/doc/optional.html).
Ich hab mich aber auf Deinen Satz bezogen:
Den tieferen Sinn des Copy-Konstruktors habe ich auch nie eingesehen - zumindest nicht bei nicht-trivialen Klassen.
Ein Copy-Ctor hat bei allen Typen, ob trivial oder nicht einen Sinn, nämlich dann, wenn man das Objekt vervielfältigen kann und das hat nix mit ner Referenz zu tun (und der Konto-Typ wird natürlich nicht vervielfältigt, wenn ein neues Konto erzeugt wird, sondern das Konto ist einem aus n endlich vielen Typen zugeordnet). Das einzige, wo sich Copy-Ctor und Referenz 'überschneiden' und in gewisser Hinsicht austauschbar sind, ist beim Funktionsaufruf:
printKonto( const Konto& konto);
oder
printKonto( Konto konto);
Aber in Fall zwei ist es eben ein Konto, das dem orginalen gleicht, aber halt nicht ein und dasselbe Konta, wie es das in Fall 1 ist.
-
Zunaechst wuerde ich die Printfunktion einfach als Methode der EinzelKonto-Klasse definieren, aber davon abgesehen, was spricht dagegen es so zu machen:
EinzelKonto::EinzelKonto (EinzelKonto *pSource = NULL) { if (pSource == NULL) SetDefaults (); else { //Uebernimm Werte } }
-
was spricht dagegen es so zu machen
prinzipiell: dass das kopieren des Kontos eine andere Syntax hat als das Kopieren eines ints.
int zahl( andereZahl);
Konto( &anderesKonto);In Deiner Umsetzung: Dass das Orginal verändert werden kann, da ein const fehlt, dass Du Dein Objekt mit 'Konto konto();' initialisieren musst statt, wie man es normalerweise macht mit 'Konto konto;', dass Du keine Initialisierungslisten verwendest ...
Außerdem: Dein Beispiel ist auch eine Art Kopierkonstruktor, nur dass er nicht konventionell ist sondern halt nen Zeiger statt der const Ref kriegt. Du hingegen sagst
Den tieferen Sinn des Copy-Konstruktors habe ich auch nie eingesehen. Wenn zwei oder mehr Instanzen einer Klasse benotige, die die gleichen Daten enthalten oder sich nur unwesentlich unterscheiden, dann baue ich eher eine weitere Klasse, die die gemeinsamen Daten beinhalten und lasse einen Pointer als Member der Klasse auf die Klasse mit den gemeinsamen Daten zeigen
du meinst also, dass sich Copy-Ctor und Zeiger irgendwie ersetzen. Tun sie aber nicht. Sind völlig verschiedene Dinge.