Zeiger oder Referenz
-
Mein Senf muss auch noch dazu.
Ich mache es oft so, dass ich durch die Wahl anzeige was in der Methode passiert.
Nimmt die Methode einen Pointer, dann kann man sich sicher sein, dass die Methode dem Paramerter verändert.
Nimmt die Methode eine (const) Referenz, dann ist man sich sicher, dass die Methode den Parameter nicht verändern kann.Wenn ich programmiere, dann weiß ich ja was die Methoden die ich aufrufe machen.
Wenn ich mir später meinen Code wieder durchsehe und Methodenaufrufe mit '&' sehe, dann weiß ich sofort.. oha.. der will einen Pointer -- gut da passen wir mal auf.
-
Also ich kann volkards Meinung in dem Punkt jetzt auch nicht ganz nachvollziehen. Vielleicht ist das aber auch Teil einer Studie und er will nur schauen wieviele Leute in den nächsten Wochen einfach seine Meinung weiterplappern. Vielleicht kommt er aber auch nachher mit dem Bombenargument und begründet seine Meinung zu Pointern.
Ob die Funktion etwas mit dem übergebenen Objekt macht erkenne ich daran ob die übergebene Referenz const ist oder nicht und Pointer benutze ich nur wenn es mit Referenzen nicht geht bzw. umständlicher wäre. Pointer auf 0 prüfen macht meiner Meinung nach nur dann Sinn wenn das Objekt auch 0 sein kann, also zusätzlich zur Zeigerfunktion einen Status darstellt (diverse Libs machen da regen Gebrauch von).
-
(naja, fast)
genau das meinte ich

-
hehejo schrieb:
Nimmt die Methode einen Pointer, dann kann man sich sicher sein, dass die Methode dem Paramerter verändert.
Nimmt die Methode eine (const) Referenz, dann ist man sich sicher, dass die Methode den Parameter nicht verändern kann.Warum macht man das nicht gleich nur mit Referenzen?
void func( const alpha& a ); // a kann nicht geaendert werden void func( alpha& a ); // a kann geaendert werden
-
Sovok schrieb:
string c = string ( "alpha" ) + string("beta"); //weil "alpha" sonst nur n zeiger is zu dem du addierstDas macht nix, weil es einen operator+( const char*, const string& ) gibt, den Volkard ja aufgelistet hat.
Also ist:string c = "alpha" + string("beta");equivalent zu:
string c( operator+( "alpha", string( "beta" ) ) );
-
Sovok schrieb:
//wohl eher string c = string ( "alpha" ) + string("beta"); //weil "alpha" sonst nur n zeiger is zu dem du addierst }Nein, siehe Volkards letzten Kommentar und seinen Auszug aus den Standard-Headerdateien.
-
groovemaster schrieb:
Trotzdem hab ich seine Ansichten bzgl. Referenzen vs. Zeiger in diesem Punkt nie nachvollziehen können. Mir ist es ehrlich gesagt zu unintuitiv, anhand semantischer Feinheiten zu erlesen, was ein Programm macht. Mir ist zB vollkommen egal, ob da
add_something(foo)oder
add_something(&foo)steht. Das einzige was mich interessiert, ist, dass something zu foo addiert wird.
sematische feinheiten? jup, die würden mich ankotzen. ich will klaren uen einfachen code.
deswegen will ich keine semantischen feinheiten ausdeuteln müssen, sondern *sofort* sehen, was los ist. insbesondere will ich *nicht* überlegen müssen, was die funktion denn bewerkstelligt. ich kann nicht immer aus dem namen genau folgern, was die funktion macht. vor allem, wenn fremde leute die funktionsnamen erfunden haben.
und das geht so:do_something(foo);//call by value, foo belib unverändertoder
do_something(&foo);//foo wird verändert, weil zeigerund sonst nix.
also beim lesen sehe ich immer ganz genau, ob ich das foo nur zeige oder ob ich's zum verändern hergebe. ach, ist das eine freude, wenn man mal ein kaputtes foo hat, und sich überlegen muß, welche funktiopn foo kaputtet hat. kann ja nur eine mit &foo ewesen sein.
außerdem bin ich auch beim programmieren ein wenig feige. sollte das nicht jeder sein? wenn ich meinen funktionen immer nur foo zu fresen gebe, hab ich echt ein besseres gefühl. die paar mal, wo es dann doch &foo sein muß, mach ich das halt, aber immer bewußt. und da weiß ich, daß keiner an meinen daten rumkritzelt, ohne daß ich das bewußt zugelassen habe.
der stil ist also sowas wie const, nur eben als forderung von der callerseite her und nicht als versprechen von der calleeseite her und ergänzt die strenge typprüfung von c++ dahingehend, daß man schon wieder einen haufen aus-versehen-fehler zu compilerfehlern gemacht hat.
-
Referenzen sind, grob gesagt, die Zeiger fuer Leute die Zeiger nicht raffen...
Man beschneidet seine Programme durch konsequente Nutzung von Referenzen selbst.
Wie prueft man denn die Gueltigkeit einer Referenz? Bei Zeigern geht das ganz einfach. Bei Referenzen ist dies unmoeglich. Zeiger koennen euren Programmen bei geschickter Nutzung zu brutaler Geschwindigkeit verhelfen. Klar, fuer operatoren-ueberladung, catchen, copy c'tor & co. sind Referenzen noetig.
Ich nutze Referenzen quasi nie ausser bei letzterem.
Aber ausserdem gibt es keine Referenz-Arithmetik...
Zeiger
-
Walli schrieb:
Ob die Funktion etwas mit dem übergebenen Objekt macht erkenne ich daran ob die übergebene Referenz const ist oder nicht
das ist aber nur die eine richtung. die funktion verspricht dir, daß da nix geändert wird. bei nur fehlerfreien funktionen, die du benutzt, mag das ok sein. wenn ich schreibe
foo(v);dann brauche ich nicht nachzuschauen, ob foo das v verändert oder nicht. ok, namen wie foo verwenet man nicht. ich brauche aber auch nicht anhand des namens zu überlegen, und das klappt eh nicht immer.
f.add(v);soll jetzt v zu f addiert werden oder f zu v? uneindeutige namen wir's immer geben. übrigens wird hier v genommen und zu f addiert, was ja klar ist, weil v nicht verändert wird.
-
volkard schrieb:
do_something(foo);//call by value, foo belib unverändert do_something(&foo);//foo wird verändert, weil zeigerEs ist genau umgekehrt:
void do_something( const string& foo ); // Call-By-Reference Parameter ("foo" ist eine Referenz) void do_something( string* foo ); // Call-By-Value Parameter ("foo" ist ein Pointer und damit ein Value!)also nochmal:
do_something(foo); // call by reference do_something(&foo); // call by (address) value
-
volkard schrieb:
ok, namen wie foo verwenet man nicht. ich brauche aber auch nicht anhand des namens zu überlegen
Genau dafür sind Namen aber da. Und ich finde, man sollte sich seine Englischkenntnisse durchaus zunutze machen, um Informationen aus dem Code zu ziehen oder welche hineinzustecken.
Dann sind folgende Fälle auch vollkommen klar definiert:
object.add(another_object); // 'another_object' wird zu 'object' hinzugefügt object.add_to(another_object); // 'object' wird zu 'another_object' hinzugefügtAndere Sprachen verfügen nicht wie C++ um eine syntaktische Kennzeichnung von Zeigern und unterstützen ausschließlich Übergaben per Wert, wobei aber durch Verwendung von Referenzobjekten die Übergabewerte trotzdem in den meisten Fällen modifiziert werden können, ohne dass dies für den Aufrufer offensichtlich ist.
Keine dieser Sprachen hat (bei korrekter Benutzung) irgendwelche Probleme mit dieser Tatsache. Wieso also sollte es in C++ Probleme damit geben?
-
+Zeiger! -Referenzen... schrieb:
Referenzen sind, grob gesagt, die Zeiger fuer Leute die Zeiger nicht raffen...
und die mehrheit der leute, die machen, was die mehrheit macht, ohne zu wissen, warum. und früher die leute, die voller spaß experimentell alles mal zu refs machen, um halt das neue zu machen.
Man beschneidet seine Programme durch konsequente Nutzung von Referenzen selbst.
stimmt.
Wie prueft man denn die Gueltigkeit einer Referenz? Bei Zeigern geht das ganz einfach. Bei Referenzen ist dies unmoeglich.
braucht man nicht.
will man ne funktion machen, die wahlweise ein objekt zurückgibt oder einen fehler meldet, kanns per exception gemacht werden. wenns ein schlimmer fehler ist. will man einfach noch dazu sagen können, daß man nix gefunden hat, nimmt man halt zeiger. beim zurückgeben alles klar.
beim übergeben? weiß nicht. muß wohl den raytracer von gestern nochmal lesen. da war viel mit 0 als übergabe. viel viel. hab mir gar nicht überlegt, wozu. also wenn ich neben dem objekt ne bounbding-box mitgeben kann, aber nicht muß. die default-bounding-box ist =0 und im tracercode steht allenthalten if(bb==0)...else...
oder ich mach ein sinnvolles default-argument, die globale boundingbox, die grundsätzlich "jup, der strahl schneidet ich" behauptet. bräuchte kein if mehr im tracercode.
ich kann mich gerade nicht erinnern, mal 0 als übergabe zugelassen zu haben.Zeiger koennen euren Programmen bei geschickter Nutzung zu brutaler Geschwindigkeit verhelfen.
da kann man zu 99% statt zeigern auch referenzen nehmen mit gleicher steigerung.
Klar, fuer operatoren-ueberladung, catchen, copy c'tor & co. sind Referenzen noetig.
Ich nutze Referenzen quasi nie ausser bei letzterem.das ist fein. weiter so.
Aber ausserdem gibt es keine Referenz-Arithmetik...

spricht für referenzen. aber ist zum glück nix schlimmes.
-
Mir zeigt meine IDE recht zuverlässig an welche Parameter eine Funktion erwartet. Insofern brauche ich keine Kennzeichnung und kann mich weiterhin darauf besinnen Referenzen einzusetzen wo sie Sinn machen. Überall Referenzen zu benutzen ist natürlich auch Quatsch! Manches geht eben mit den "guten alten" Pointern besser/einfacher.
-
Zitat:
Zeiger koennen euren Programmen bei geschickter Nutzung zu brutaler Geschwindigkeit verhelfen.da kann man zu 99% statt zeigern auch referenzen nehmen mit gleicher steigerung.
Nein, weil...
Zitat:
Aber ausserdem gibt es keine Referenz-Arithmetik...
spricht für referenzen. aber ist zum glück nix schlimmes.
du das wohl falsch verstanden hast. Zeiger-Arithmetik, traegt bei geschickter Nutzung einen guten Teil zum Geschwindigkeitsvorsprung gegenueber allem anderen bei. Hier noch 4 Punkte.. gegen Referenzen, fuer Zeiger!
1.) Sobald Bibliotheken und Komponenten Dritter hinzukommen, ist es
ziemlich wahrscheinlich, dass man ploetzlich doch irgendwo einen
Zeiger hat. Sobald ein Programm Zeiger verwendet, koennen
Referenzen ploetzlich auch theoretisch undefiniert sein.
Das kann man aber nicht pruefen.2.) Es gibt keine Referenzarithmetik. Sobald eine Variable irgendeine
Menge von Objekten durchlaufen soll, muessen immer Container und
Iteratoren zum Einsatz kommen. Aus rein objektorientierter Sicht
ist das ehrlich gesagt natuerlich viel sauberer, erzeugt aber auch
deutlich mehr und komplexeren Kode.3.) Es gibt keine Felder von Referenzen. Schlaegt in die gleiche Kerbe
wie 2.): Fuer Sammlungen und Mengen sind jetzt immer Objekte
notwendig. Selbst die Standard-Funktion
main(int nArgs, char *apcArgs[])
liesse sich nicht einfach durch
main(int nArgs, char &apcArgs[])
ersetzen.4.) Es gibt keine Referenzen auf Referenzen. Gut, mehr ein Detail, aber
ein Unterschied....referenzen->baeh!
-
Konrad Rudolph schrieb:
Genau dafür sind Namen aber da. Und ich finde, man sollte sich seine Englischkenntnisse durchaus zunutze machen, um Informationen aus dem Code zu ziehen oder welche hineinzustecken.
und wenn die nicht reichen, programmiert man auf deutsch.
Dann sind folgende Fälle auch vollkommen klar definiert:
object.add(another_object); // 'another_object' wird zu 'object' hinzugefügt object.add_to(another_object); // 'object' wird zu 'another_object' hinzugefügtdie welt ist nicht so einfach. bei deinem eigenen code mag das noch klappen. aber du arbeitest immer mit nasenbären zusammen und kannst von glück reden, wenn alle mindestens so gut lesbaren code wie Power Off schreiben. das betrifft vor allem die namenswahl. einfache syntaktische regeln wie "zeiger beim ändern, referenzen bei const" kann man durchsetzen, aber gute namensvergabe kannste nicht durchsetzen.
Andere Sprachen verfügen nicht wie C++ um eine syntaktische Kennzeichnung von Zeigern und unterstützen ausschließlich Übergaben per Wert, wobei aber durch Verwendung von Referenzobjekten die Übergabewerte trotzdem in den meisten Fällen modifiziert werden können, ohne dass dies für den Aufrufer offensichtlich ist.
Keine dieser Sprachen hat (bei korrekter Benutzung) irgendwelche Probleme mit dieser Tatsache. Wieso also sollte es in C++ Probleme damit geben?keine probleme der brisanz, daß allein deswegen regelmäßig proejekte sterben.
andere sprachen verfügen über keine klassen. und man kann mit ihnen coden und hat keine probleme.
andere sprachen verfügen über keine templates und man kann mit ihnen coden ohne probleme.
andere sprachen verfügen über kein const und man kann mit ihnen coden ohne const zu benutzen und sieht keine probleme.
und du siehst hier keine probleme, weil dur die vorteile auf der anderen seite noch nicht erfahren hast und sie dir nicht von hier aus ausmalen kannst. ich kenne beide seiten und sehe den vorteil.
mach doch mal ein jahr lang meinen stil und entscheide dann. kannst hier nicht viel mehr machen, als zu experimentieren. ich hab auch neulich erst meinen bracing style geändert und bin von .h nach .hpp umgestiegen. unter anderem weil oft-recht-habende leute (obwohl sie in der minderheit waren) das empfohlen haben, hab ich mich aufs experiment eingelassen und hab umgestellt und vorzüge entdeckt, die ich als außenstehender nicht sah.
-
Walli schrieb:
Mir zeigt meine IDE recht zuverlässig an welche Parameter eine Funktion erwartet. Insofern brauche ich keine Kennzeichnung und kann mich weiterhin darauf besinnen Referenzen einzusetzen wo sie Sinn machen. Überall Referenzen zu benutzen ist natürlich auch Quatsch! Manches geht eben mit den "guten alten" Pointern besser/einfacher.
ack. Auch ich kann hier volkard's Meinung nicht ganz nachvollziehen. Der erste wichtige Punkt ist, dass ich eher selten Funktionen habe, die ihre Parameter verändern. So etwas ist unmathematisch und lässt sich nicht in Ausdrücken verwenden. Erinnert mich verdächtig an COM-ge***e, wo das Ergebnis immer by ref zurückgegeben wird und als Rückgabewert HRESULT.
Auch wenn man mehrere Werte zurückgeben will gibt es oft schönere Möglichkeiten, als Parameter zu verändern. Es ist nicht immer verkehrt, Parameter zu verändern, aber ich mache das eher selten und in diesen Fällen achte ich darauf, dass man auch ohne Adressoperator diese Situation erkennen kann. Dazu kann beispielsweise ein guter Funktionsname beitragen. Fazit: IMHO braucht man kein "Warn-&" auf der Aufrufer-Seite und wenn das der einzige Grund ist, nen Zeiger zu verwenden, bin ich nicht überzeugt.
Zeiger als Parameter sind IMHO dann angesagt, wenn man auf der Aufrufer-Seite ständig mit Zeigern hantiert. Wenn ich irgendwie ständig mit LinkedListNode arbeite, wäre es affig, den Zeiger dereferenziert zu übergeben. Meistens ist sowas ja eher intern in einer LinkedList-Klasse, so dass man schon wissen kann, ob Zeiger oder Referenz sinnvoller ist.
-
@+Zeiger! -Referenzen...: Bloß weil Referenzen kein ultimativer Pointerersatz sind macht sie das nicht überflüssig. Ich finde die beiden ergänzen sich hervorragend. Man braucht keine Felder von Referenzen und auch keine Referenzen auf Referenzen, weil es Pointer gibt. Und bei einem Pointer kannst du auch nicht immer prüfen ob er ins leere zeigt.
-
Walli schrieb:
Mir zeigt meine IDE recht zuverlässig an welche Parameter eine Funktion erwartet.
beim tippen ein vortiel, aber beim lesen? gehste wirklich mit der maus auf jeden funktionsnamen und schaust die paremeter an, wenn du eine funktion nochmal liest?
-
Wenn ich befürchte, dass ich den Code später nicht mehr verstehen könnte mache ich einen Kommentar. Wo ist das Problem?
-
Optimizer schrieb:
Der erste wichtige Punkt ist, dass ich eher selten Funktionen habe, die ihre Parameter verändern. So etwas ist unmathematisch und lässt sich nicht in Ausdrücken verwenden.
ok. ganz wichtiger punkt. veränderliche parameter sind selten und deshalb ist die ganze diskussion nicht wichtig.
dennoch sollte man doch überlegen, was nu besser ist in den seltenen fällen, wo man doch veränderliche parameter hat. und da hab ich argumente gebracht.IMHO braucht man kein "Warn-&" auf der Aufrufer-Seite und wenn das der einzige Grund ist, nen Zeiger zu verwenden, bin ich nicht überzeugt.
aber du hast dem warn-& nur entgegengestellt, daß veränderliche parameter selten sein sollten (spricht weder dafür noch dagegen), und daß man besser namen nehmen sollte (definitiv das schwierigste zu erreichende ziel). da bleib ich auf dem teppich und führe halt was sinnvolles ein, weil das fernziel mir nic mehr zu meinen lebzeiten bringt.
Zeiger als Parameter sind IMHO dann angesagt, wenn man auf der Aufrufer-Seite ständig mit Zeigern hantiert. Wenn ich irgendwie ständig mit LinkedListNode* arbeite, wäre es affig, den Zeiger dereferenziert zu übergeben.
das haste ein konzept nicht verstanden. wie in der grapehntheorie üblich, besteht ein netz aus knoten und kanten. auch unsere bäume und listen.
class Liste{ struct Knoten; Knoten* Kante; struct Knoten{ Kante kanteZumNachfolger; }; };und weil man dauernd mit Kanten hantiert, übergibt man auch dauernd Kanten. nur macht man hier selten den typedef. ist ja auhc nicht notwendig.
der code für einen einfachen binären sortierbaum füllt ein paar seiten. vor allem die 12! fallunterscheidungen i remove() sind schlimm. hier hab ich den typedef Kante wirksam eingesetzt und auch mit Zeigern auf Kanten hantiert (die suche nach einem wert gibt die Kante zurück, an der der wert hängt oder hängen sollte, wodurch ich lecker die selbe funktion für einfügen, löschen und suchen verwenden kann. ok, geht auch mit Knoten**, aber da ist zu früh denkblockade, um auf guten code zu kommen). resultat: der code paßte auf zwei bildschirmseiten.