Wann Referenzen, wann Pointer?
-
Ihr tut immer alle rum von wegen Intellisense, Intellisense, Intellisense und seid dann nicht in der Lage, euch den Funktionskopf der auzurufenden Funktion, der ja meist automatisch eingeblendet wird, anzugucken? ohje...
Meist erkennt man ja schon im Funktionkopf an der Referenz, dem Pointer, oder der const Referenz, ob das Objekt möglicherweise in der Funktion geändert wird, oder nicht geändert wird.
-
Man kann auch seine Funktionsnamen so wählen, dass man sieht, ob die Funktion etwas am übergebenen Objekt ändert. Ich persönlich finde das ja doch übersichtlicher...
-
Bis vor einiger Zeit habe ich es genau so gehalten, ich habe diese Argumente vor Jahren gelesen und fand sie logisch.
Mittlerweile nutze ich aber auch konstante und nichtkonstante Referenzen, Zeiger sind bei mir eine Garantie dafür, dass sie auch 0 sein dürfen.
Ist imho auch eine ganz gute Konvention und spart das ständige gegen 0 testen.
Außerdem muss man davon ausgehen, dass eine Funktion, die nichtkonstante Parameter als Referenz entgegen nimmt, diese auch ändert.
-
It0101 schrieb:
Ihr tut immer alle rum von wegen Intellisense, Intellisense, Intellisense und seid dann nicht in der Lage, euch den Funktionskopf der auzurufenden Funktion, der ja meist automatisch eingeblendet wird, anzugucken? ohje...
Meist erkennt man ja schon im Funktionkopf an der Referenz, dem Pointer, oder der const Referenz, ob das Objekt möglicherweise in der Funktion geändert wird, oder nicht geändert wird.
Für Intellisense müßte ich mit der Maus rumfahren. Naja, ich benutze weder Intellisense noch Debugger, aber das ist ein anderes Thema.
Den Funktionskopf muß ich nichtmal angucken bei meinem Stil. Ich sehe es schon am Aufruf. Das ist lecker.Und daß ich mal einem Zeigerparameter erlauben würde, NULL zu sein, das kommt glaube ich nur in der rekursiven Baumtraversierung vor und sonst gar nirgends. Deswegen brauche ich dafür keine Unterscheidung.
-
yahendrik schrieb:
Ist imho auch eine ganz gute Konvention und spart das ständige gegen 0 testen.
Zirkelschluß. Man soll nicht jeden übergebenen Zeiger gegen 0 testen, denn Zeiger bedeuten nicht automatisch, daß der Anwender das Recht hätte, 0 zu übergeben. Was soll auch 0 bedeuten? Daß ich ein Objekt nicht anlegen konnte, daß ich die Ini-Datei nicht lesen konnte? Da ist doch längst eine Exception geflogen. Die Suchfunktionen geben auch nicht mehr 0 zurück, sondern end(). Wie schubse ich mir in C++ eine 0 ins Programm? Und wozu?
Dein ständiges Geteste entspringt dem Gedanken, Zeiger würden das aussagen, und taugt daher nicht mehr viel, um genau den Gedanken zu begründen.
-
Bei mir kommt das schon hinundwieder mal vor, dass Pointer NULL sind.
Denn es ist ja oft auch eine Aussage, dass z.B. bestimmte Daten nicht geliefert wurden, oder fehlerhaft geliefert wurden, so dass der Empfänger dann mit NULL auch eine Aussage geliefert bekommt. Klar kann man das auch anders machen, aber NULL langt meist.
-
Belli schrieb:
Wenn ich einen Pointer übergebe, dann weiß ich, daß das Objekt, auf das gezeigt wird, geändert werden kann, ohne dass ich mir die Deklaration/Definition der aufgerufenen Funktion ansehen muss.
Man kann sowohl den Zeiger als auch das Objekt auf das gezeigt wird als Parameter konstant deklarieren, ein Zeiger alleine ist daher noch kein Garant dafür das man etwas ändert. Eine gewisse Sicherheit gibt daher nur der Blick auf die Funktionsdeklaration.
Belli schrieb:
Wenn ich ein Objekt übergebe, dann sehe ich an der Aufrufstelle nicht, ob die aufgerufene Funktion das als Objekt oder als Referenz nimmt.
Genauso wenig wie du davon ableiten kannst ob es verändert wird oder nicht, und das gilt auch für den Zeiger.
Belli schrieb:
Bei der Entwicklung ist mir das noch relativ egal, wenn ich aber nach längerer Zeit den Quellcode ansehen muss, dann finde ich es hilfreich, wenn ich mich darauf verlassen kann, dass ein übergebenes Objekt nach Rückkehr noch genau so aussieht wie vor dem Aufruf.
Ich auch, nur setze ich dafür auf const, und nicht auf Referenz vs. Zeiger, zumal letzteres ja durchaus beabsichtigt sein kann wenn 0 zulässig ist.
Belli schrieb:
Deshalb halte ich es grundsätzlich wie Volkard:
Pointer, wenn die Funktion den Paramter ändert, anderenfalls konstante Referenzen.Für mich gilt wiederum: Zeiger wo nötig, Referenzen wo (sinnvoll) möglich und const wenn etwas nicht geändert wird (Bei Zeigern jeweils mit Unterscheidung der Adresse oder des Wertes). Dafür dient das Schlüsselwort const ja schließlich auch.
-
It0101 schrieb:
Bei mir kommt das schon hinundwieder mal vor, dass Pointer NULL sind.
Denn es ist ja oft auch eine Aussage, dass z.B. bestimmte Daten nicht geliefert wurden, oder fehlerhaft geliefert wurden, so dass der Empfänger dann mit NULL auch eine Aussage geliefert bekommt. Klar kann man das auch anders machen, aber NULL langt meist.Also muß man C-Stil proggern, um eurer Stil-Argumentation folgen zu können?
Das erkärt natürlich alles.Nein, das erklärt auch nichts.
Nur, weilFoo* f=loadFoo();
fehlschlägt, mache ich doch nicht danach
workFoo(f);
, sondern teste sofort
if(f==0)
und mache zum Beispiel
if(!f) f=vreateDefaultFoo(); if(!f) teminexit();
Außerdem würde ich nicht
workFoo(f);
schreiben, sondern
f->work();
und hoffentlich mache ich nicht
if(!this)
(Die ganze Zeit Angst hab, daß destroyFoo(f) vergessen wird, wenn eine Exception fliegt. Wenn mir doch bloß was einfallen würde, wie ich das elegant vermeide.)
-
Wenn ich mich beim Design meiner Funktionen an die erwähnte Vorgehensweise halte, dann habe ich keine Zeiger auf konstante Objekte als Funktionsparameter.
Und dann sehe ich direkt an der Aufrufstelle, dass mein Objekt verändert werden könnte, wenn ich einen Zeiger übergeben muss.
Wenn ich stattdessen ein Objekt übergebe, weiß ich nicht, ob ich eine Kopie oder eine Referenz übergebe, ohne auf die Funktionsdeklaration zu sehen - es sei denn, ich verfahre wie erläutert.
Ich will nach einem halben Jahr nicht erst überlegen, in welcher Datei die aufgerufene Funktion deklariert ist, die Datei öffnen und mir den Funktionskopf ansehen müssen, um zu wissen, ob ich erwarten kann, dass ich mein Objekt unverändert zurückbekomme.Tachyons Vorschlag, dass mit dem Funktionsnamen kenntlich zu machen, könnte ich evtl. noch folgen, wenn ich kreativ genug für entsprechende Namensfindungen wäre.
-
@Volkard: ich neige dazu, in fast allen funktionen, die übergebenen Variablen zu prüfen. Und wenn besagter Funktion die Übergabewerte nicht in den Kram passen ( z.B. Null-Pointer ), dann steht es ihr frei eine Exception zu werfen oder mittels entsprechender Rückgabewerte vergleichbares kundzutun.
Denn was tust du, wenn du einem anderen Entwickler deine Funktion, die ihre Variablen nicht prüft, zur Verfügung stellst und selbiger sie anwendet ohne ohne zu ahnen was das für Konsequenzen haben könnte.
*kabooom*
-
It0101 schrieb:
Denn was tust du, wenn du einem anderen Entwickler deine Funktion, die ihre Variablen nicht prüft, zur Verfügung stellst und selbiger sie anwendet ohne ohne zu ahnen was das für Konsequenzen haben könnte.
*kabooom*
Jup. Und das ist voll ok so. Ich bin kein Kindermädchen. Also doch schon, aber nicht so extrem.
Mal ein fürchterliches Beispiel:bool parseIniFile(FILE* file,std::map<string,string>* settings)
Wer da NULL übergibt, hat doch einen Sockenschuss. Der soll Landschaftsgärtner werden, da muß er nicht so viel mit Computern arbeiten.
Außerdem wäre der Mitarbeiter doch schon lange von strlen, strcat und strcpy rausgeprüft worden. Ich denke, der Mitarbeiter ist hypothetisch und keine reale Gefahr.
-
volkard schrieb:
Mal ein fürchterliches Beispiel:
bool parseIniFile(FILE* file,std::map<string,string>* settings)
Wer da NULL übergibt, hat doch einen Sockenschuss.
Schönes Beispiel. Warum sollte man hier die Map nicht als Referenz nehmen? Bei dem Funktionsnamen denkt doch niemand, die Map sei ein Input-Parameter. Und aus dem gleichen Grund, aus dem man weiß, dass die Map mit Werten befüllt wird, weiß man auch, dass man nicht NULL übergeben darf. Ich jedenfalls finde den Aufruf schöner, wenn ich nicht noch ein hässliches
&
vor die Variable setzen muss.
-
ipsec schrieb:
Bei dem Funktionsnamen denkt doch niemand, die Map sei ein Input-Parameter. Und aus dem gleichen Grund, aus dem man weiß, dass die Map mit Werten befüllt wird, weiß man auch, dass man nicht NULL übergeben darf.
Ok. Beide Stilrichtungen sind also nicht tragend, weil aus dem hübschen Funktionsnamen genug hervorgeht. So muß es auch sein. Schlechte Funktionsnamen akzeptiere ich nicht als Argument.
ipsec schrieb:
Ich jedenfalls finde den Aufruf schöner, wenn ich nicht noch ein hässliches
&
vor die Variable setzen muss.Ok, wenn es häßlich ist...
Mir gefällt es, weil ich beim Anschauen des Codes am & schon sehe, daß dort reingeschrieben wird. Sozusagen schon aus Entfernung, beim leichten drüberfliegen, noch bevor ich den Funktionsnamen ganz lese. Vielleicht habe ich gar keine Lust, immer wieder den Sinn meines Codes zu durchdenken und mag lieber möglichst viel automatisch erfassen ohne auf den Sinn einzugehen.
Aber sowas wie
bool parseIniFile(FILE* file,std::map<string,string>* settings)
wird man bei mir eh nicht finden.
Eher
Settings::Settings(char const* iniFileName);
und natürlich prüfe ich nicht, ob so ein Hallodri mir als iniFileName einen Nullzeiger übergibt. Das wäre mir einfach zu javaesk.
-
volkard schrieb:
Settings::Settings(char const* iniFileName);
Also sowas würde ich auch nicht prüfen... Man kann ja nun wirklich nicht jedem Arsch nachtragen...
Besser wäre da dann wohl:
Settings::Settings( const std::string &iniFileName );
-
It0101 schrieb:
Besser wäre da dann wohl:
Settings::Settings( const std::string &iniFileName );
Mit dem Aufruf
Settings s("grangi.ini");//Hab ja doch nur ein Literal
und dem Code
{ ifstream in(iniFileName.c_str());//Brauche ja doch nur einen char* ...
-
volkard schrieb:
It0101 schrieb:
Besser wäre da dann wohl:
Settings::Settings( const std::string &iniFileName );
Mit dem Aufruf
Settings s("grangi.ini");//Hab ja doch nur ein Literal
und dem Code
{ ifstream in(iniFileName.c_str());//Brauche ja doch nur einen char* ...
Genau da ziehe ich aber die const Ref. auf std::string vor.
Ausserdem halte ich es für einen Fehler in der Standard Lib. dass ifstream ein const char* als File Name nimmt und nicht ein std::string.
-
theta schrieb:
Ausserdem halte ich es für einen Fehler in der Standard Lib. dass ifstream ein const char* als File Name nimmt und nicht ein std::string.
Wir müssen ja nicht alles mit Gewalt langsam machen. Ich denke, das tut der Sprache auch ein bißchen weh.
Aber ich würde es begrüßen, wenn es beide Überladungen gäbe.
-
Mit C++0x wird es beide Überladungen geben, soweit ich weiß.
-
314159265358979 schrieb:
Mit C++0x wird es beide Überladungen geben, soweit ich weiß.
Wichter fände ich es, wenn die fstreams mit einem wchar_t als char-Typ für den Dateinamen klar kommen würde.
-
Tachyon schrieb:
Wichter fände ich es, wenn die fstreams mit einem wchar_t als char-Typ für den Dateinamen klar kommen würde.
wchar_t für den namen, aber char für den Inhalt? Dann müssten auch diverse andere Kombinationen angeboten werden.