Wann Referenzen, wann Pointer?
-
Hallo.
Ich stehe öfters vor der Wahl, ob ich einen übergabeparameter per referenz oder als pointer konfiguriere und wollte da mal eure Meinung hören, was ihr "besseren Stil" findet, wann ihr was verwendet und ob es vll auch von der semantik her unterschiede gibt.
Also erstmal beschreibe ich, wie ich das im Moment mache:
Wenn ich etwas übergeben will das nicht verändert werden braucht oder nicht verändert werden soll, dann mache ich das stets per const referenz.
Wenn ich etwas übergebe, was geändert werden soll (z.B. als zusätzlichen rückgabewert), dann habe ich das bisher immer per normaler referenz gemacht.
Allerdings ist das gerade der Punkt an dem ich unsicher bin, weil dann der Aufrufer es übersehen könnte, dass es geändert werden kann, weswegen da ein pointer vll doch sinnvoll ist, damit der aufrufer explizit den adressoperator benutzen muss und falls er das vergisst er etwas bemerkt.Ich bin mal gespannt auf eure Meinungen!
-
Eigentlich immer eine Referenz ausser wo es nicht geht.
-
MrRef schrieb:
Allerdings ist das gerade der Punkt an dem ich unsicher bin, weil dann der Aufrufer es übersehen könnte, dass es geändert werden kann, weswegen da ein pointer vll doch sinnvoll ist, damit der aufrufer explizit den adressoperator benutzen muss und falls er das vergisst er etwas bemerkt.
Es gibt Leute, welche dieses Vorgehen wählen. Ich persönlich halte nicht viel davon. Denn was ist, wenn man bereits einen Zeiger in der Hand hat? Oder was ist, wenn ein Zeiger auf ein konstantes Objekt erwartet wird? Dann muss man den Adressoperator verwenden, obwohl das Objekt nicht verändert wird. Solche Vorgehensweisen verwirren eher, als das sie helfen. Man missbraucht dann die Syntax der Sprache.
Gründsätzlich verwende ich Zeiger dann, wenn der Parameter auch NULL sein darf und sonst eine Referenz. In seltenen Fällen kommt auch ein Zeiger zum Einsatz, obwohl der Parameter nicht NULL sein darf, aber weil es im Arbeitsablauf aus irgendeinem Grund besser reinpasst. Kommt aber eher selten vor.
Grüssli
-
MrRef schrieb:
Allerdings ist das gerade der Punkt an dem ich unsicher bin, weil dann der Aufrufer es übersehen könnte, dass es geändert werden kann, weswegen da ein pointer vll doch sinnvoll ist, damit der aufrufer explizit den adressoperator benutzen muss und falls er das vergisst er etwas bemerkt.
So benutze ich den Adressoperator bei Aufrufen auch. Als Markierung, daß die Funktion den Pointee ändern kann.
-
Dravere schrieb:
Es gibt Leute, welche dieses Vorgehen wählen. Ich persönlich halte nicht viel davon. Denn was ist, wenn man bereits einen Zeiger in der Hand hat?
Dann mache ich wohl
print(*p);
Oder was ist, wenn ein Zeiger auf ein konstantes Objekt erwartet wird?
Dann hätte ich mich bei der Funktion nicht an den Stil gehalten und pr0ggerte durcheinander. Klar wäre das nicht hübsch.
-
Meistens ist auch so klar, dass ein bestimmter Parameter geändert wird.
getline(cin, str);
Ansonsten wäre es eventuell ratsam, die Funktion treffender zu bennenen. Zur Not tuts eben auch ein Pointer.
-
ipsec schrieb:
Meistens ist auch so klar, dass ein bestimmter Parameter geändert wird.
getline(cin, str);
Ansonsten wäre es eventuell ratsam, die Funktion treffender zu bennenen. Zur Not tuts eben auch ein Pointer.
Meistens ist ebenso klar, ob man NULL übergeben darf oder nicht.
Und das schlimmste daran ist, man steht selten vor der Frage. Wir meiden ja zuzätzliche Returnwerte und so Sachen, wo man es überhaupt bräuchte.
-
volkard schrieb:
Dravere schrieb:
Es gibt Leute, welche dieses Vorgehen wählen. Ich persönlich halte nicht viel davon. Denn was ist, wenn man bereits einen Zeiger in der Hand hat?
Dann mache ich wohl
print(*p);
Ich meinte eigentlich, wenn du einen Zeiger in der Hand hast und ihn an eine Funktion übergibst, welche das Objekt verändert. Dann kannst du keinen Adressoperator hinschreiben und trotzdem wird das Objekt verändert.
Ich will schlussendlich eigentlich darauf hinaus, dass du mit dieser Vorgehensweise nicht eindeutig festlegen kannst, ob nun ein Parameter verändert wird oder nicht. Also kann man sich nicht darauf verlassen und muss sich den Code oder die Dokumentation trotzdem genau anschauen. Der Zweck ist somit nicht vorhanden und du hinderst dich damit nur selbst.
Grüssli
-
ok, danke schonma für die zahlreichen Antworten.
Also mal zusammenfassend:
Kann man dann sagen, dass bei C++ übergabe eines Pointers per Value nie wirklich sinnvoll ist?
-
MrRef schrieb:
Kann man dann sagen, dass bei C++ übergabe eines Pointers per Value nie wirklich sinnvoll ist?
"Nie" ist viel zu streng. Aber wenn eine Referenz passt, dann nimm die Referenz. Dadurch bleiben am Ende nur noch wenige Fälle für Zeiger übrig.
Grüssli
-
Referenz = Objekt behält immer seine Gültigkeit
Pointer = Objekt kann NULL seinBeispiel:
void print(const std::string& str); // Objekt ist immer gültig void setUserdata(UserData *data); // UserData ist optional und kann auch nicht vorhanden sein (=NULL)
-
volkard schrieb:
Als Markierung, daß die Funktion den Pointee ändern kann.
Wo ist da der Witz?
-
Also wenn ich das Objekt nicht ändere und es nicht vom integrierten Typ ist, nehme ich eine const Referenz. Wenn ich es ändere, vermeide ich auch meistens Zeiger und wähle eher die Referenz, wobei ich mir über den Ansatz der hier diskutiert wird auch schon einige Gedanken gemacht habe. Letztendlich macht es aber meines Wissens nach zumindest für den Maschienencode keinen großen Unterschied, da der Compiler bei Referenzen oft selber aber Zeiger nutzt. Jedoch könnte man wie hier schon gesagt wurde natürlich zwecks der Semantik drüber nachdenken...ich bleibe aber bei den Referenzen.
Lg freeG
-
volkard schrieb:
So benutze ich den Adressoperator bei Aufrufen auch. Als Markierung, daß die Funktion den Pointee ändern kann.
Dann mach Dir ne Funktion die es kenntlich macht.
template<typename T> T & this_parameter_may_be_doomed_to_be_changed_by_this_fucking_dangerous_function_or_method(T & obj){ return obj; } class doof { void warum(deshalb & d); }; doof d; deshalb okay; d.warum(this_parameter_may_be_doomed_to_be_changed_by_this_fucking_dangerous_function_or_method(okay));
Dann wird alles klar.
-
Denk EOutOfResources hat es schon passend ausgedrueckt.
Referenzen wo man kann,
Pointer wo man "muss"Damit reduziert sich die anwendung von Zeigern an diesen Stellen auf ein paar wenige valide Gruende:
- Ungueltige Objekte. Nicht jedes Object kann ich in der Programmlogik so bauen, das es quasi einen ungültigkeitswert bekommt. Da referenzen immer gueltig sein muessen, könnt ich mit denen nicht signalisieren "Hallo, lass mich in ruhe, ich bin eigentlich ungueltig". Dafür bieten sich dann pointer an !
- Kompatiblitaet zu vorherigen Code. Wenn jemand vor mir (ne Bib oder Framework) schon pointer wegen dem ungeultigkeitszeug verwendet, wuerde es unnötig komplex werden, das auf referenzen runterzubrechen.
- Ab und an braucht man eine variable, die zur Laufzeit auf unterschiedliche Objecte Zeigt, wo das Object quasi ausgetauscht wird ... versuch das mal mit referenzen und sauberen C++ Stil
Ciao ...
-
RHBaum schrieb:
versuch das mal mit referenzen und sauberen C++ Stil
Der Stil wird durch Referenzen nicht sauberer.
Ihr überstrapaziert die Referenzen. Das ist die selbe Schiene wie das Verwenden von std::string für *alle* Zeichenketten, weil es "mehr C++" ist. Nix gut.
Die Referenzen mußten für die Operatorenüberladung erfunden werden. Ok, jetzt haben wir sie. Aber den Haupt-Code verschlimmern sie leider. Deswegen empfiehlt Onkel Volkard: Sparsam einsetzen.Meine Debugsession sieht so aus:
c=siquo(b,c); bar.foo(a,&b);//patsch, NUR hier kann b kaputtgegangen sein print(b);
-
Meine Debugsession sieht so aus:
Ich setze einen Watchpoint aufb
und lasse den Debugger die Arbeit für mich erledigenIm übrigen hast du den Satz von RHBaum völlig aus dem Kontext gerissen. Und wieso deine Debugsession nicht unbedingt funktionieren muss, wurde auch schon aufgezeigt. Zudem hat hier überhaupt niemand empfohlen, dass man ausschliesslich Referenzen einsetzen soll. Bei
std::string
gilt auch, dass man ihn meistens nur in Spezialfällen durch einchar const*
oder ähnliches ersetzen kann.Grüssli
-
Dravere schrieb:
Meine Debugsession sieht so aus:
Ich setze einen Watchpoint aufb
und lasse den Debugger die Arbeit für mich erledigenKopf ist schneller als Debugger.
Dravere schrieb:
Im übrigen hast du den Satz von RHBaum völlig aus dem Kontext gerissen.
Nö. Sein Konstrukt zieht die Verwendung von Referenzen und den guten C++-Stil rhetorisch in eine Nähe, die ich so nicht stehen lassen wollte.
Dravere schrieb:
Und wieso deine Debugsession nicht unbedingt funktionieren muss, wurde auch schon aufgezeigt.
Kein Mittel ist erschöpfend.
Dravere schrieb:
Zudem hat hier überhaupt niemand empfohlen, dass man ausschliesslich Referenzen einsetzen soll.
Doch, RHBaum war das.
Dravere schrieb:
Bei
std::string
gilt auch, dass man ihn meistens nur in Spezialfällen durch einchar const*
oder ähnliches ersetzen kann.Stringliterale (Spezialfälle) sind char-Arrays. Ich brauche einen Grund, sie zu string zu machen.
-
volkard schrieb:
Kopf ist schneller als Debugger.
Und macht mehr Fehler, siehe auch ->
volkard schrieb:
Kein Mittel ist erschöpfend.
volkard schrieb:
Nö. Sein Konstrukt zieht die Verwendung von Referenzen und den guten C++-Stil rhetorisch in eine Nähe, die ich so nicht stehen lassen wollte.
... sogar wenn du damit nur Spass machst, ist es einfach nur blöd.
volkard schrieb:
Doch, RHBaum war das.
Kauf dir eine Brille ...
volkard schrieb:
Stringliterale (Spezialfälle) sind char-Arrays. Ich brauche einen Grund, sie zu string zu machen.
Um damit zu arbeiten?
Das ist mir zu blöd. Wenn man keine Argumente mehr hat ...
Grüssli
-
volkard schrieb:
RHBaum schrieb:
versuch das mal mit referenzen und sauberen C++ Stil
Der Stil wird durch Referenzen nicht sauberer. Ihr überstrapaziert die Referenzen.
Sehe ich anders. Wenn ich Zeiger übergebe ist NULL zulässig, und das ist zumindest bei den überwiegenden Fall in den Programmen, eher die Ausnahme als die Regel. Einer Referenz sieht man an, das ein Wert erwartet wird, einem Zeiger nicht. Einen Zeiger muss man zudem eigentlich immer vor der Verwendung prüfen. Ich glaube daher schon, das Referenzen immer Zeigern vorzuziehen sind, wenn man letztere nicht benötigt.
volkard schrieb:
Das ist die selbe Schiene wie das Verwenden von std::string für *alle* Zeichenketten, weil es "mehr C++" ist. Nix gut.
Ich sehe in der überwiegenden Mehrzahl keine Gründe die für char-Arrays gegenüber (einer wie auch immer gearteten) Stringklasse sprechen.
-
volkard schrieb:
Aber den Haupt-Code verschlimmern sie leider.
Warum? Nur weil du nicht von außen siehst wo eine Variable geändert wird?