Zeiger oder Referenz
-
@+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.
-
Walli schrieb:
Wenn ich befürchte, dass ich den Code später nicht mehr verstehen könnte mache ich einen Kommentar. Wo ist das Problem?
schau, was kommentare helfen:
http://www.c-plusplus.net/forum/viewtopic-var-t-is-121401-and-postdays-is-0-and-postorder-is-asc-and-start-is-30.html
-
volkard schrieb:
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.
... oder kasachisch. Ich meinte es ernst: Bezeichner sind schließlich dazu da, Informationen zu tragen -- wozu denn sonst? Wäre das nicht der Fall, dann würde doch nichts gegen Stroustrups April-Vorschlag sprechen, nur noch Einbuchstabenbezeichner zuzulassen.
die 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.
Gut, zum Glück (?) habe ich auf dieem Gebiet noch nicht allzu viele Erfahrungen gemacht, ich habe erst ein wirkliches Teamprojekt hinter mir (und nicht in C++) und dort konnte ich einen recht guten Kompromiss für Namenskonventionen durchdrücken.
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.Ich finde die Vergleiche denkbar unpassend. Natürlich haben verschiedene Sprachen verschiedene Möglichkeiten. Aber ich redete jetzt durchaus von Sprachen, welche zumindest grundlegende Ähnlichkeiten Aufweisen (Klassen, generische Datentypen ...). All solche Eigenschaften geben eventuell Vorteile die man in der Tat erst sieht, wenn man sie einmal ausprobiert hat. Im Kontext von C++ auf Klassen verzichten zu müssen wäre z.B. ziemlich blöd. Selbes gilt für Templates oder für 'const'. In diesem konkreten Fall sehe ich den Nachteil durch Verzicht nicht. Aber:
mach doch mal ein jahr lang meinen stil und entscheide dann.
Ich muss ganz ehrlich sagen, ich habe in C++ fast nie eine Funktion geschrieben, welche einen Parameter verändern musste, daher habe ich auch keine Ansicht dazu, welcher Stil besser ist. Ich werde wohl mal die Zeiger-Variante ausprobieren.
-
+Zeiger! -Referenzen... schrieb:
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.Wie du selber geschrieben hast, wurde das beschriebene Problem ja erst durch Zeiger verursacht. :p
Und "Referenzen baeh" dann hast net verstanden warum es die gibt.
Es wurden aber auch nicht umsonst die Zeiger nicht verbannt. Beides hat seinen Sinn, beides brauch man(oder sollte man zumindest benutzen).
-
@Pellaeon: haettest du meinen vorherigen Post gelesen, wuesstest du das es selbst meiner Meinung nach, in bestimmten Faellen Referenzen das sinnvollste sind im allgemeinen Einsatz allerdings nicht.
+Zeiger! -Referenzen... schrieb:
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.Wie du selber geschrieben hast, wurde das beschriebene Problem ja erst durch Zeiger verursacht. :p
Ja, wenn du das aber sagst solltest du bedenken das Referenzen nach Zeigern kamen, da viele Leute schlicht zu dumm fuer Zeiger waren. Demnach kann man nach deiner Aussage keine Bibliotheken nutzen die vor Referenzen da waren nur weil man sich vor den wesentlich effektiveren Zeigern scheut..

Ich sag jetzt nix mehr dazu, ich bin froh das ich verstanden hab wo ich Zeiger und wo Referenzen nutze und mir durch Faulheit o.A. nichts versaue, verschlechtere, verlangsamere... :p
-
Sach ma wie kommst du drauf das Referenzen langsamer sind???
-
@+Zeiger! -Referenzen...:
Ich welchem Bereich bist du denn Tätig, dass die Performance derart kritisch ist?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.Wenn du weißt, dass ein Zeiger möglicherweise auf NULL zeigt wirst du ihn nicht dereferenzieren ohne es vorher geprüft zu haben. Und ohne ihn zu dereferenzieren kannst du keine Referenz erzeugen => FALSCH.
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.Referenzen sind keine Iteratoren. Wow. Das ist auch nicht ihre Aufgabe. Eine Referenz ist ein Verweis oder ein Alias auf ein anderes Objekt. Genau das ist der Sinn einer Referenz.
Iteratoren haben einen anderen Zweck. Ob der Iterator jetzt den Typ "std::list<Foo>::iterator" oder "Bar *" hat ist doch völlig nebensächlich, da ich ihn eh meistens durch irgendwelche begin() oder end()-Methoden erhalte. Ein Iterator ist ein Konzept. Guck dir mal an, was boost::array<>::begin() liefert. Und dann überleg mal, ob es dich interessiert, dass es sich dabei um einen Zeiger handelt.Gäbe es Referenz-Arithmetik wäre sie kein Alias mehr für ein bestimmtes Objekt und würde ihre Aufgabe verfehlen.
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.Bring bitte ein praxistaugliches Beispiel, in dem ein Feld von Referenzen Sinn machen würde.
4.) Es gibt keine Referenzen auf Referenzen. Gut, mehr ein Detail, aber
ein Unterschied.Referenzen auf Referenzen würde keinen Sinn machen. Was ist das denn für ein Dämliches Argument? Und Unterschiede sind generell schlecht? Am besten alles wäre gleich? es gibt auch einen Unterscied zwischen int und char. Wie grausam.

-
+Zeiger! -Referenzen... schrieb:
Ja, wenn du das aber sagst solltest du bedenken das Referenzen nach Zeigern kamen, da viele Leute schlicht zu dumm fuer Zeiger waren.
Genau. Das hatte garnichts mit der Notwendigkeit zu tun (operator[], Copy-C'tor, ...). Das war nur für die Dummen. Denn alles, was neu erfunden wird ist nur für die Dummen. Autos sind ja auch nur für Leute erfunden worden, die zu dumm sind mit einer Pferdekutsche zu fahren.
+Zeiger! -Referenzen... schrieb:
Demnach kann man nach deiner Aussage keine Bibliotheken nutzen die vor Referenzen da waren nur weil man sich vor den wesentlich effektiveren Zeigern scheut..

Inwiefern effektiver? Mehr schreibarbeit, weniger Klarheit im Code?
Ich sag jetzt nix mehr dazu, ich bin froh das ich verstanden hab wo ich Zeiger und wo Referenzen nutze und mir durch Faulheit o.A. nichts versaue, verschlechtere, verlangsamere... :p
Es ist schön, das du dich selbst verstanden hast, sonst würde ich dich auch sehr bemittleiden.
Aber wieso ist es Faulheit Referenzen da einzusetzen, wo sie Sinn machen? Wenn ich sicher bin, dass Sie auf ein konkretes Objekt verweisen soll, wo liegt der Vorteil eines Zeigers? Adress-Arithmetik bringt nichts (gibt nur ein Objekt), Möglichkeit auf null zu zeigen Bringt nix (ich weiß, das ein konkretes Objekt vorliegt), Zeiger auf Zeiger bringt nix (ich kann einen Anlegen, aber wozu?).
Was genau ist in einem solchen Fall der unglaubliche Vorteil des Zeigers? Das ich öfters ein Sternchen Tippen muss, um den Zeiger zu dereferenzieren? Das mir die klare Aussage, dass es sich um ein konkretes Objekt handelt verloren geht und höchstens durch ein (eigentlich überflüssiges) Kommentar wiedergebracht werden könnte?
-
volkard schrieb:
Walli schrieb:
Wenn ich befürchte, dass ich den Code später nicht mehr verstehen könnte mache ich einen Kommentar. Wo ist das Problem?
schau, was kommentare helfen:
http://www.c-plusplus.net/forum/viewtopic-var-t-is-121401-and-postdays-is-0-and-postorder-is-asc-and-start-is-30.htmlJa, das ist schon eine gefährliche Sprache. Da kann man selbst mit Kommentaren Unsinn machen
.Btw: Was unterscheidet eigentlich jetzt genau einen Kommentar von deiner Art der Kennzeichnung? Du hast durch die Zeigerlösung ja auch keinen Mehrwert außer dem '&', welches dir anzeigt, dass der übergebene Parameter modifiziert wird.
-
Nach volkard's Modell programmierte String-Funktion:
#include <string> using std::string; void func( string* dest, const string& src ) { dest->operator+=( src ); }Bzw.:
void func( string* dest, const string& src ) { *dest += src; }Mit Referenzen:
void func( string& dest, const string& src ) { dest += src; }
-
Volkard dein Argument gilt aber nur für Objekte die auf dem Stack liegen, sobald ich das Objekt auf dem Heap habe, oder selbst nur nen Zeiger habe, dann ist ein foo( bar ); keine Eindeutigkeit mehr für ein veränderbarer Parameter.
Und wer ein v.add( f ); schreibt, das v auf f addiert, der gehört kräftig angebrüllt. Wenn auf f was addiert werden soll, dann schreibt man das f.add( v ); und nicht umgekehrt.
-
Natürlich hat man selbst etwas mehr zu schreiben, aber der Aufrufer weißt dann gleich Bescheid.
Muss er ein '&' schreiben, wird die Methode bestimmt seinen Wert verändern.
-
hehejo schrieb:
Natürlich hat man selbst etwas mehr zu schreiben, aber der Aufrufer weißt dann gleich Bescheid.
Muss er ein '&' schreiben, wird die Methode bestimmt seinen Wert verändern.Das muss nicht so sein:
void func( const A* a ) { /* ... */ } A b; func( &b ); // !!!!!!!!!!
-
Power Off schrieb:
Nach volkard's Modell programmierte String-Funktion:
#include <string> using std::string; void func( string* dest, const string& src ) { dest->operator+=( src ); }Bzw.:
void func( string* dest, const string& src ) { *dest += src; }Mit Referenzen:
void func( string& dest, const string& src ) { dest += src; }
zeig auch die aufrufe, damit der sinn erkennbar ist.
func(&a,b);a wird offensichtlich verändert und b nicht.
veränderliche parameter sind eh fast immer mist. also ist das anzuwarnen doch voll klug. so wie auch die lange schreibweise von reinterpret_cast und konsorten absichtlich lang ist.
-
User--- schrieb:
Und wer ein v.add( f ); schreibt, das v auf f addiert, der gehört kräftig angebrüllt. Wenn auf f was addiert werden soll, dann schreibt man das f.add( v ); und nicht umgekehrt.
a.print(b) macht was?
es ist leider die pur virtuelle void print(ostream&), die man braucht, um polymorphe objekte mit op<< auszugeben.
klar hieß die beim borland ursprünglich printAt, aber heute heißt sie meistens nur print. die namen verschlechtern sich also messbar. hab ich jetzt recht?
-
Walli schrieb:
Btw: Was unterscheidet eigentlich jetzt genau einen Kommentar von deiner Art der Kennzeichnung? Du hast durch die Zeigerlösung ja auch keinen Mehrwert außer dem '&', welches dir anzeigt, dass der übergebene Parameter modifiziert wird.
was ist an irgendeinem stil mehr als kommentar, wenn du es ganz eng nimmst?
das warn-& ist ein wenig mehr als kommentar, weil es im code steht und weil es was wichtiges vermittelt. und bei löschen des & gibts compilerfehler! es ist mehr als nur warnung, es ist ein automatisches verhüterli. kommentare hingegen sind doof und die liest man nicht und sie sind natürlichsprachlich.
-
User--- schrieb:
Volkard dein Argument gilt aber nur für Objekte die auf dem Stack liegen, sobald ich das Objekt auf dem Heap habe, oder selbst nur nen Zeiger habe, dann ist ein foo( bar ); keine Eindeutigkeit mehr für ein veränderbarer Parameter.
warum sollte ich nicht
Student* ps=new Student; read(ps);//verändert print(*ps);//nicht verändertschreiben?