Was ist für euch guter Programmcode?
-
ness schrieb:
Jetzt mal eine ehrlich gemenínte Frage: habt ihr jetzt nur kommentare hinzugefügt oder standen die vo anfang an da. Wenn das nämlich so ist sollte ich meinen "Kommentierungsstil" schnell ändern...
Ich schreibe wenig Kommentare, aber viel Dokumentation. Das ist einfach schlechte Erfahrung mit zuwenig Dokumentation... das dort oben ist mein üblicher Dokumentierstil.
-
ness schrieb:
Jetzt mal eine ehrlich gemenínte Frage: habt ihr jetzt nur kommentare hinzugefügt oder standen die vo anfang an da. Wenn das nämlich so ist sollte ich meinen "Kommentierungsstil" schnell ändern...
Dokumentieren tue ich eigentlich immer ganz gut, wenn ich nicht gerade besonders schlecht gelaunt bin. Meisten reifen die Doc-Kommentare dann auch erst im Laufe der Zeit richtig aus, weswegen z.B. Shade gleich mal entdeckt hat, dass mein file kein Dateiname, sondern ein Pfad zu einer Datei ist.
Mit der Zeit finde ich solche Sachen dann und bessere sie aus.Kommentare im Source findet man bei mir eh nur spärlich, ich hab jetzt sogar einige noch raus, seit dem ich das gepostet habe - zu trivial.
-
also das einzige was mich an mecnels code wirklich stört ist das er deutsch und englisch mischt. das macht einen ja kirre wenn man sich das anguckt
und zu JBeni
/** * Gibt dasjenige Element zurück, auf das sich derzeit alle verschickten * Events beziehen. * @return Das Element, auf das sich alle Events beziehen. */ public Element getElement(){ return element; } /** * Setzt das Element, auf das sich alle durch <code>fireX</code> verschickten * Events beziehen. * @param element Das Element */ protected void setElement( Element element ){ this.element = element; } /** * Nicht jedes Element ist interessant genug um ein Event zu verschicken. * Diese Methode entscheidet, für welche Elemente Events versendet werden. * @param element Das zu prüfende Element * @return true, wenn Events dieses Element betreffend verschickt * werden soll */ protected boolean isInteresting( Element element ){ return true; }
die kommentare von get und setElement sind irgendwie unnötig, sollte selbstsprechend sein. und eigentlich kannst du element ja public machen, es wird doch eh nichts überprüft.
bei isInteresting fehlt wohl noch ein "// todo!!!" oder?
-
thx
borg schrieb:
die kommentare von get und setElement sind irgendwie unnötig, sollte selbstsprechend sein. und eigentlich kannst du element ja public machen, es wird doch eh nichts überprüft.
Na, 1. sind public Variablen hässlich (man kann den Aufbau nur noch schwer ändern), 2. kann nicht jeder das Element setzen, der Setter ist protected (ich finde nichts schlimmer als private, das hat so den Geschmack "stop, ich bin nicht erweiterbar, stop, mein Verhalten ist festgelegt, stop", aber protected Variablen gefallen mir auch nicht...).
Hm, jaa, notwendig sind sie wirklich nicht, aber ohne sehen die Methode so nackt aus...borg schrieb:
bei isInteresting fehlt wohl noch ein "// todo!!!" oder?
Das war Absicht, wenn mal jemand nur an Elementen die z.B. HTML-Links sind, interessiert ist, kann er so ganz einfach eine Unterklasse die nur auf HTML-Links reagiert schreiben.
Das sollte ich wohl in die Doku schreiben.
-
Der Thread hat mir gezeigt das kommentare deutlich wichtiger sind als ich bisher dachte...
Ich habe mal einen unvollständigen code von mir rausgezerrt und die deklaration kommentiert:#pragma once #include <TypeTraits.h> #include <cassert> #include <vector> #include <algorithm> #include <fstream> #include <ctime> #include <cstdlib> #include <string> #include <sstream> //genetic_algorithm.h - entält eine über templates möglichst konfigurierbare Klasse für genetische Algorithemn sowie Standardvorgaben //Hier stehen die Standardvorgaben in Form von Funktionen, nicht gezeigt template < class individual, //Klasse, die die Individuen repräsentiert; Individue werden immer mit new erzeugt und mit delete gelöscht class evaluation_type, //Typ, mit dem die Bewerungsfunktionen arbeiten; Annahme: keine hohen kopierkosten unsigned how_much, //Startmenge der Population evaluation_type (*evaluator)(individual&), //Bewerungsfunktion bool (*is_better)(typename Loki::TypeTraits<evaluation_type>::ParameterType,typename Loki::TypeTraits<evaluation_type>::ParameterType), //Vergleichsfunktion für Individuen evaluation_type perfect, //Wenn die Bewrtungsfunktion diesen Wert zurückgibt, wird das individuum als perfekt angesehen individual* (*generator)(), //generiert zufällige Individuen individual* (*select_to_recombine)(std::vector<std::pair<individual*,evaluation_type> >&), //Wählt aus dem übergebenen Vektor ein zu rekombiniernendes Individuum aus //(wird also immer mindestens paarweise aufgerufen) individual* (*select_to_mutate)(std::vector<std::pair<individual*,evaluation_type> >&), //Wählt aus dem übergebenen Vektor ein zu mutiernedes Individuum aus individual* (*mutator)(individual&), //mutiert individuen; es wird ein neues individuum zurückgegeben individual* (*recombine)(individual&,individual&), //rekombiniert zwei individuen; es wird ein neues zurückgegeben unsigned (*mutate_count)(unsigned), //wieviele Individuen sollen mutiert werden? übergeben wird die größe der Population nach der Bewertung unsigned (*recombine_count)(unsigned), //dito, für rekombinierende Individuen void(*selector)(std::vector<std::pair<individual*,evaluation_type> >&,std::vector<std::pair<individual*,evaluation_type> >&,std::vector<std::pair<individual*,evaluation_type> >&), //übergeben werden die Ausgangsindividuen, die rekombinierten Individuen und die mutierten Individuen; die Ausgangsmenge ist mit einer neuen Ausgangsmenge zu befüllen; //die vektoren aus mutierten und rekombinierten Individuen werden nach dem Aufruf dieser Funktion gelöscht bool verbose > class genetic_algorithm { private: //das beste derzeit vorhandene Individuum, Element von Indivs std::pair<individual*,evaluation_type>best; //die Aktuellen Individuen std::vector<std::pair<individual*,evaluation_type> > indivs; //Hilfsfunktion - führt eine komplette Runde durch (Bewerten, Mutieren, Rekombinieren, Selektiern, Aktualisieren) //aktualisiert perfect_found; gibt perfect_found zurück bool one_time(); bool perfect_found; //Hilfsfunktion - bewertet den gesamten Vektor, füllt evaluation_type aus void evaluate(std::vector<std::pair<individual*,evaluation_type> >& input); //wenn verbose gesetzt ist, wird hier die Anzahl bereits duchgeführter Runden gezählt unsigned counter; genetic_algorithm(const genetic_algorithm&); //noch nicht implementiert, wird irgendwann public const genetic_algorithm& operator=(const genetic_algorithm&); //dito public: std::pair<individual*,evaluation_type> get_best() {if(best.first==0)throw std::runtime_error("Not available!");else return best;}; void reset(); bool perfect_indiv_found()const{return perfect_found;}; //arbeitet den Algo so lange ab, bis ein Individuum mit Gefunden ist, für das die Bewertungsfunktion finish zurückgibt; //führt nicht mehr als count runden aus, wenn count 0 ist wird solange gearbeitet bis Bedingung 1 erfüllt ist individual *start(evaluation_type finish,unsigned count); ///arbeitet den Algo count Runden ab oder bis ein perf. indiv. gef. wurde; wenn count=0 solange bis ein perfektes indiv gefunden wurde individual *do_for(unsigned count); //arbeitet den Algo so lange ab wie ein Aufruf von pwhile true zurückgibt oder ein perfektes Individuum gefunden wurde - hilfreich bei multithreading??? individual *do_while(bool(*pwhile)()); //arbeitet den Algo so lange ab, bis ein perfektes Individuum gefunden wurde -> mit Vorsicht zu genießen individual *do_until_perfect_found(); genetic_algorithm(); ~genetic_algorithm(); std::string print()const; std::vector<std::pair<individual*,evaluation_type> >&get_impl_ref(){return indivs;}; unsigned get_counter()const {if(!verbose)throw std::runtime_error("Verbose-flag not set!");else return counter;}; std::ostream& inner_operator_out(std::ostream& o); //mein compiler meckert wenn ich versuche das über friends zu lösen std::istream& inner_operator_in(std::istream& o); //dito };
Ich versuche halt immer möglichst aussagekräftige Namen zu benutzen um mich um die Kommentare zu drücken...
-
ness: Schreib deine Kommentare im Template nicht einfach hintendran, sondern davor, weil du sonst zu weit in die Breite gehst. Dann würd ich noch ne menge Leerzeilen reinbauen (z.B. immer nach nem Kommentar mit ner Funktionsdeklaration). Dann noch ein paar Leerzeichen (hinter //, zwischen () und const).
Inhaltlich hab ichs mir noch nicht angeguckt.
-
Shade Of Mine schrieb:
Und was sagst du zu meiner anderen Kritik? Ich bin gerne bereit offen zu diskutieren.
So, dann werde ich eben Schlüsse aus der Kritik ziehen.
1.) Verwende keine defines, denn es ist nicht sinnvoll, das gesamte äußere Erscheinungsbild einer Anwendung einfach dadurch verändern zu können, dass man den symbolischen Konstanten andere Werte gibt. Besser ist es, an hundert verschiedenen Positionen irgendwo in hunderten Modulen die tatsächlichen Werte hinzuschreiben, denn wenn man dann dahinter kommt, dass die Textgroesse doch um 2 logische Einheiten größer sein soll, dann hat man das Vergnügen, dutzende Dateien durchsuchen zu müssen und den Wert eben hundertmal ausbessern zu dürfen, und das gleiche nochmal, wenn doch die kleiner Textgröße gereicht hätte. So schlägst du wenigstens Stunden an Zeit tot, was zwar mit define nicht nötig gewesen wäre (da hätte es gereicht den einen Wert daneben auszubessern), aber wie auch immer.
2.) Funktionen sollten ihrem Aufrufer generell keine Auskunft darüber geben, ob während ihrer Ausführung irgend welche Fehler aufgetreten sind, darum sollte man vor allem keine bool Werte zurückgeben, denn dann könnte man ja womöglich den Fehler bei Probeläufen sofort entdecken. Es macht einfach mehr Spaß, in einem großen Programm unter hunderten schweigenden Minifunktionen einen Fehler finden zu müssen.
3.) Verwende keine Zeiger auf Zeiger, denn ... warum eigentlich? Ach ja, das spart nämlich sehr viel Speicher und das ist <template Begruendung bitte> auch nicht gut.
4.) Als Elementfunktionen überladene arithmetische Operatoren sind nicht gut. Die sind nämlich zu wenig fehleranfällig, besser du überlädst sie global und lässt dich davon überraschen, was unter Mitwirkung Deiner Konvertierungskonstruktoren oft an interessanten Ergebnissen dabei heraus kommt. Ist einfach zu lustig (ach und explicit ist zwar alles andere Plattformunabhängig, aber wer braucht das schon bei Klassen für mathematische Objekte? Sind ja sowieso alle Plattformspezifisch).
------
Aus dem besagten Header wurde schliesslich ein Hauptprogramm, an dessen
Bezeichnern alleine man erkennt, was das Programm macht#include"MTdef.h" HINSTANCE instanz; MTactics DasSpiel; // Endlich - die gesamte Anwendung in ein Objekt gepackt LRESULT CALLBACK MessageHandler(HWND hwnd,UINT imsg, WPARAM wParam,LPARAM lParam){ switch(imsg){ case WM_CREATE: if(!DasSpiel.Create(instanz,hwnd)) MessageBox(NULL,"Fehler","Fehler",0); break; case WM_PAINT: DasSpiel.ShowAll(); break; case WM_COMMAND: DasSpiel.ProcessClick(wParam); if(DasSpiel.DoFast){ DasSpiel.DoFast=false; DasSpiel.ProcessClick(DasSpiel.reserve); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd,imsg,wParam,lParam); } int WINAPI WinMain(HINSTANCE hinst,HINSTANCE phinst, LPSTR commline,int showstyle){ // usw usf. - das Übliche das hier steht
// Dieses Programm ist selbsterklärend, wie ich denke.
// Alles was passiert, ist, dass das Spiel erzeugt, mitunter neu gezeichnet
// wird, und schliesslich dass es Benutzer Eingaben beliebigen Typs zu ver-
// arbeiten imstande ist. Genau wie jedes andere Programm auch.--- Ein Projekt kann man frontal angehen, also einfach drauflosprogrammieren,
--- wenn einem der Code in einer Funktion zu lang wird, einen Funktionsnamen
--- dafür erfinden, weiterprogrammieren, wieder einen Funktionsnamen erfinden
--- und so weiter und so fort. Irgendwann hat man dann hunderte Funktionen,
--- wahrscheinlich auch eine Lösung für das Problem, und kann sogar von
--- Modularisierung sprechen.--- Oder man kann es aus simplen Bausteinen wie etwa Vektoren, Feldern,
--- Spielsteinen etc., die alle Elemente des Programmes sind, allmählich zu
--- einem Ganzen zusammenfügen. Und letzteres heisst dann objektorientiert.--- Gute Nacht
-
Bitte hör auf. Das ist ja schrecklich.
-
MecnelsNichtEingeloggt schrieb:
1.) Verwende keine defines, denn es ist nicht sinnvoll, das gesamte äußere Erscheinungsbild einer Anwendung einfach dadurch verändern zu können, dass man den symbolischen Konstanten andere Werte gibt. Besser ist es, an hundert verschiedenen Positionen irgendwo in hunderten Modulen die tatsächlichen Werte hinzuschreiben
Verarscht du mich?
2.) Funktionen sollten ihrem Aufrufer generell keine Auskunft darüber geben, ob während ihrer Ausführung irgend welche Fehler aufgetreten sind, darum sollte man vor allem keine bool Werte zurückgeben,
Weil es Exception gibt?
3.) Verwende keine Zeiger auf Zeiger, denn ... warum eigentlich? Ach ja, das spart nämlich sehr viel Speicher und das ist <template Begruendung bitte> auch nicht gut.
Man würde natürlich nie irgendetwas wrappen, damit es leichter verwendbar ist, oder?
4.) Als Elementfunktionen überladene arithmetische Operatoren sind nicht gut. Die sind nämlich zu wenig fehleranfällig, besser du überlädst sie global und lässt dich davon überraschen, was unter Mitwirkung Deiner Konvertierungskonstruktoren oft an interessanten Ergebnissen dabei heraus kommt.
Lies bei zeiten mal ein C++ Buch. Wie etwa Effective C++ oder ähnliches.
(ach und explicit ist zwar alles andere Plattformunabhängig, aber wer braucht das schon bei Klassen für mathematische Objekte? Sind ja sowieso alle Plattformspezifisch).
Ok, du verarscht mich.
Falls du der echte bist (bitte demnächst durch einloggen bestätigen/widerlegen) beende ich die Diskussion mit dir. Denn verarschen lasse ich mich nicht.--- Ein Projekt kann man frontal angehen, also einfach drauflosprogrammieren,
--- wenn einem der Code in einer Funktion zu lang wird, einen Funktionsnamen
--- dafür erfinden, weiterprogrammieren, wieder einen Funktionsnamen erfinden
--- und so weiter und so fort. Irgendwann hat man dann hunderte Funktionen,
--- wahrscheinlich auch eine Lösung für das Problem, und kann sogar von
--- Modularisierung sprechen.--- Oder man kann es aus simplen Bausteinen wie etwa Vektoren, Feldern,
--- Spielsteinen etc., die alle Elemente des Programmes sind, allmählich zu
--- einem Ganzen zusammenfügen. Und letzteres heisst dann objektorientiert.*lol* muss ich das echt kommentieren?
Ich kann bei mir in der Arbeit jeden fragen den ich will, die Antwort wird sein: kleine Funktionen, kleine Module mit kleinen öffentlichen Schnittstellen sind wesentlich wartbarer als ein paar monster funktionen.
Hier in dem Forum sind auch so ziemlich alle meiner Meinung (es gab noch keine Gegenstimme)Denk bei zeiten mal darüber nach.
Natürlich heißt dass es alle so machen nicht, dass es richtig ist. Aber man sollte die Gründe überlegen warum so viele Leute soviel zeit dafür opfern kompakte Funktionen und Module zu entwickeln, wenn es doch einfacher und besser wäre ohne plan alles in eine Funktion zu stecken...
-
MecnelsNichtEingeloggt schrieb:
1.) Verwende keine defines, denn es ist nicht sinnvoll, das gesamte äußere Erscheinungsbild einer Anwendung einfach dadurch verändern zu können, dass man den symbolischen Konstanten andere Werte gibt. Besser ist es, an hundert verschiedenen Positionen irgendwo in hunderten Modulen die tatsächlichen Werte hinzuschreiben, denn wenn man dann dahinter kommt, dass die Textgroesse doch um 2 logische Einheiten größer sein soll, dann hat man das Vergnügen, dutzende Dateien durchsuchen zu müssen und den Wert eben hundertmal ausbessern zu dürfen, und das gleiche nochmal, wenn doch die kleiner Textgröße gereicht hätte. So schlägst du wenigstens Stunden an Zeit tot, was zwar mit define nicht nötig gewesen wäre (da hätte es gereicht den einen Wert daneben auszubessern), aber
das hat keiner gesagt, du sollst "const int GROESSE = 100;" schreiben statt "#DEFINE GROESSE 100". den vorteil solltest du als erfahrener hase doch sofort erkennen.
MecnelsNichtEingeloggt schrieb:
2.) Funktionen sollten ihrem Aufrufer generell keine Auskunft darüber geben, ob während ihrer Ausführung irgend welche Fehler aufgetreten sind, darum sollte man vor allem keine bool Werte zurückgeben, denn dann könnte man ja womöglich den Fehler bei Probeläufen sofort entdecken. Es macht einfach mehr Spaß, in einem großen Programm unter hunderten schweigenden Minifunktionen einen Fehler finden zu müssen.
das hat keiner gesagt, du sollst exceptions verwenden. die sind dafür gemacht.
MecnelsNichtEingeloggt schrieb:
3.) Verwende keine Zeiger auf Zeiger, denn ... warum eigentlich? Ach ja, das spart nämlich sehr viel Speicher und das ist <template Begruendung bitte> auch nicht gut.
das hat keiner gesagt, du sollst std::vector o.ä. container verwenden um schwer auffindbare fehler zu vermeiden.
MecnelsNichtEingeloggt schrieb:
4.) Als Elementfunktionen überladene arithmetische Operatoren sind nicht gut. Die sind nämlich zu wenig fehleranfällig, besser du überlädst sie global und lässt dich davon überraschen, was unter Mitwirkung Deiner Konvertierungskonstruktoren oft an interessanten Ergebnissen dabei heraus kommt. Ist einfach zu lustig (ach und explicit ist zwar alles andere Plattformunabhängig, aber wer braucht das schon bei Klassen für mathematische Objekte? Sind ja sowieso alle Plattformspezifisch).
hä? "ich weiß irgendwas nicht genau, evtl. hatte ich unrecht, also behaupte ich einfach irgendwas" oder wie?
-
Shade Of Mine schrieb:
MecnelsNichtEingeloggt schrieb:
1.) Verwende keine defines, denn es ist nicht sinnvoll, das gesamte äußere Erscheinungsbild einer Anwendung einfach dadurch verändern zu können, dass man den symbolischen Konstanten andere Werte gibt. Besser ist es, an hundert verschiedenen Positionen irgendwo in hunderten Modulen die tatsächlichen Werte hinzuschreiben
Verarscht du mich?
2.) Funktionen sollten ihrem Aufrufer generell keine Auskunft darüber geben, ob während ihrer Ausführung irgend welche Fehler aufgetreten sind, darum sollte man vor allem keine bool Werte zurückgeben,
Weil es Exception gibt?
3.) Verwende keine Zeiger auf Zeiger, denn ... warum eigentlich? Ach ja, das spart nämlich sehr viel Speicher und das ist <template Begruendung bitte> auch nicht gut.
Man würde natürlich nie irgendetwas wrappen, damit es leichter verwendbar ist, oder?
4.) Als Elementfunktionen überladene arithmetische Operatoren sind nicht gut. Die sind nämlich zu wenig fehleranfällig, besser du überlädst sie global und lässt dich davon überraschen, was unter Mitwirkung Deiner Konvertierungskonstruktoren oft an interessanten Ergebnissen dabei heraus kommt.
Lies bei zeiten mal ein C++ Buch. Wie etwa Effective C++ oder ähnliches.
(ach und explicit ist zwar alles andere Plattformunabhängig, aber wer braucht das schon bei Klassen für mathematische Objekte? Sind ja sowieso alle Plattformspezifisch).
Ok, du verarscht mich.
Falls du der echte bist (bitte demnächst durch einloggen bestätigen/widerlegen) beende ich die Diskussion mit dir. Denn verarschen lasse ich mich nicht.--- Ein Projekt kann man frontal angehen, also einfach drauflosprogrammieren,
--- wenn einem der Code in einer Funktion zu lang wird, einen Funktionsnamen
--- dafür erfinden, weiterprogrammieren, wieder einen Funktionsnamen erfinden
--- und so weiter und so fort. Irgendwann hat man dann hunderte Funktionen,
--- wahrscheinlich auch eine Lösung für das Problem, und kann sogar von
--- Modularisierung sprechen.--- Oder man kann es aus simplen Bausteinen wie etwa Vektoren, Feldern,
--- Spielsteinen etc., die alle Elemente des Programmes sind, allmählich zu
--- einem Ganzen zusammenfügen. Und letzteres heisst dann objektorientiert.*lol* muss ich das echt kommentieren?
Ich kann bei mir in der Arbeit jeden fragen den ich will, die Antwort wird sein: kleine Funktionen, kleine Module mit kleinen öffentlichen Schnittstellen sind wesentlich wartbarer als ein paar monster funktionen.
Hier in dem Forum sind auch so ziemlich alle meiner Meinung (es gab noch keine Gegenstimme)Denk bei zeiten mal darüber nach.
Natürlich heißt dass es alle so machen nicht, dass es richtig ist. Aber man sollte die Gründe überlegen warum so viele Leute soviel zeit dafür opfern kompakte Funktionen und Module zu entwickeln, wenn es doch einfacher und besser wäre ohne plan alles in eine Funktion zu stecken...
#defines haben ihre Vorteile und ihre Nachteile.
Konstanten haben ihre Vorteile und ihre Nachteile.
Was man nimmt ist Geschmackssache, mir sind #defines lieber,
Dir vielleicht Konstanten, aber wie gesagt, egal.
Ich hab den Zynismus vielleicht ein bisschen zu weit getrieben, sorry.Wie man Operatoren überlädt ist ebenfalls von der Aufgabe abhängig,
die man damit lösen soll. Wenn Vektoren im Spiel sind, ist es meiner
Meinung besser, Elementfunktionen zu nehmen, Du bevorzugst globale
Operatoren und baust lieber explicit Constructors ein. Schon wieder:
Nur Geschmackssache. Viele Wege führen nach Rom, nur mein Ansatz ist
wahrscheinlich bequemer und weniger fehleranfällig. Aber wie gesagt -
nur meine Meinung.bool Rückgabe vs Exceptions: Exceptions sollten in krassen Ausnahmefällen zum Einsatz kommen (Ein Speichermedium existiert nicht und es wird versucht, darauf zu schreiben, um ein Beispiel zu nennen), aber wohl nicht, um Abkürzungen zu anderen Programmteilen zu nehmen. Das erinnert an Basic und Goto. Meine Meinung, darum schweigen meine Funktionen nicht darüber, ob sie eine Operation erfolgreich beenden konnten, sondern lassen es den Aufrufer mit true oder false wissen. Wieder: Geschmackssache. Rückgabewerte für Fehler ist die ältere Methode, aber sicher nicht schlechter (eher bewährter).
Zeiger auf Zeiger: Nicht jeder kommt mit der Zeigerarithmetik zu recht, dabei ist es doch so simpel. Wenn sie Dir aber zu kompliziert sind, dann kannst Du angesichts der heutzutage zur Verfügung stehenden Hauptspeichergrößen natürlich auch jedes Objekt extra im Speicher anlegen. Dieser Umgang mit dem Hauptspeicher wäre vor 20 Jahren katastrophal gewesen, und auch heute gibt es Anwendungen, in denen das nicht egal ist (aber die haben wahrscheinlich etwas mit der NASA oder ähnlichen abstrusen Vereinen zu tun).
Sollte ich Dir mit meiner letzten Post zu sehr auf den Schlips getreten sein, so musst Du schon zugeben, dass das angesichts Deiner Kommentare zu meinen Posts nicht von ungefähr gekommen ist. Trotzdem - war nicht böse gemeint.
Viele kleine Bausteine verknüpft mit Logik ergeben ein geordnetes Ganzes.
--- ein bekannter Kybernetiker der 60er Jahre_Vordenker der ObjektorientierungGute Nacht.
-
Kann es sein das dieser Mecnels ein Troll ist und alle ihn abfüttern?
-
Vielleicht wäre es aber mal an der Zeit sich von seinen "bewährten" Verfahren zu lösen und Neuland zu betreten?
Kannst Du mal kurz erklären, warum Deine überladenen Operatoren Members sein sollen? Dann hast Du die implizite Konvertierung sofern nicht durch explicit unterdrück ja immer noch, halt auf dem rechten Argument. Das heißt a+b funktioniert dann, aber b+a nicht... das klingt irgendwie nicht so wirklich sinnvoll, oder?
Und wenn Du das verhinderst indem Du den Konstruktor explicit machst... dann sehe ich nicht was es bringen soll, das als Member zu machen.
-
#defines haben ihre Vorteile und ihre Nachteile.
Konstanten haben ihre Vorteile und ihre Nachteile.
Was man nimmt ist Geschmackssache, mir sind #defines lieber,
Dir vielleicht Konstanten, aber wie gesagt, egal.
Ich hab den Zynismus vielleicht ein bisschen zu weit getrieben, sorry.Du hast wirklich noch kein C++ Buch gelesen. Meyers, Effective C++, Die erste Richtlinie überhaupt.
Das erinnert an Basic und Goto.
Das was du tust, erinnert daran
Rückgabewerte für Fehler ist die ältere Methode, aber sicher nicht schlechter (eher bewährter).
Starrsinn³
wäre vor 20 Jahren katastrophal gewesen
Ich denke dieser Satz ist bezeichnend für deinen ganzen tollen Programmierstil...
-
Mecnels schrieb:
#defines haben ihre Vorteile und ihre Nachteile.
Konstanten haben ihre Vorteile und ihre Nachteile.
Was man nimmt ist Geschmackssache, mir sind #defines lieber,
Dir vielleicht Konstanten, aber wie gesagt, egal.naja, welche vorteile haben denn symbolische konstanten gegenüber echten? daß man die typsicherheit umgeht oder was? sag mal, da bin ich echt gespannt, was ich alles verpaßt habe, seit ich kein #define mehr verwende...
Mecnels schrieb:
Viele kleine Bausteine verknüpft mit Logik ergeben ein geordnetes Ganzes.
ist absolut richtig. dein problem dabei ist, daß du unfähig bist, diese alte erkenntnis auf die programmierung mit c++ anzuwenden. denn jeder der vielen kleinen bausteine ist entweder klasse oder funktion. in dem moment, in dem du eine funktion lauter verschiedene sachen selbst erledigen läßt, hast du aber eben nicht viele kleine bausteine, sondern wenige klobige. siehst du das denn nicht selber?!?
-
Jester schrieb:
Vielleicht wäre es aber mal an der Zeit sich von seinen "bewährten" Verfahren zu lösen und Neuland zu betreten?
Kannst Du mal kurz erklären, warum Deine überladenen Operatoren Members sein sollen? Dann hast Du die implizite Konvertierung sofern nicht durch explicit unterdrück ja immer noch, halt auf dem rechten Argument. Das heißt a+b funktioniert dann, aber b+a nicht... das klingt irgendwie nicht so wirklich sinnvoll, oder?
Und wenn Du das verhinderst indem Du den Konstruktor explicit machst... dann sehe ich nicht was es bringen soll, das als Member zu machen.Der eigentliche Punkt ist dass ein Konvertierungskonstruktor Zahl->Vektor für einen Vektor schon unsinnig ist, ob jetzt explicit oder nicht spielt da keine Rolle. In Anbetracht dessen ist es VÖLLIG EGAL ob das + jetzt Elementfunktion oder global ist, und mir deshalb die Kritik an der Implementierung als Elementfunktion sehr suspekt (weil wieder einmal kritisiert wurde ohne zu überlegen). Zahl und Vektor haben soviel gemeinsam wie Auto und Regenwurm, und niemand konvertiert Autos in Regenwürmer.
-
Mecnels schrieb:
#defines haben ihre Vorteile und ihre Nachteile.
Konstanten haben ihre Vorteile und ihre Nachteile.Vorteile von #defines wären?
zB in effective C++ super nachlesbar warum #define hier nur nachteile hat (betonung auf _nur_)Wie man Operatoren überlädt ist ebenfalls von der Aufgabe abhängig,
die man damit lösen soll. Wenn Vektoren im Spiel sind, ist es meiner
Meinung besser, Elementfunktionen zu nehmen, Du bevorzugst globale
Operatoren und baust lieber explicit Constructors ein. Schon wieder:
Nur Geschmackssache. Viele Wege führen nach Rom, nur mein Ansatz ist
wahrscheinlich bequemer und weniger fehleranfällig. Aber wie gesagt -
nur meine Meinung.Klar, dein Ansatz ist besser
Dein Problem ist doch: wenn der Ctor nicht explicit ist, hast du nicht nur beim op+ diese Konvertierungsprobleme, sondern bei Funktionsaufrufen auch.Natürlich macht es in dieser Situation keinen großen Unterschied, aber es hat keinen Vorteil den op+ als member zu deklarieren, es bricht nur die konsistenz.
Oder willst du mir nochmal genau erklären warum der op+ member sein muss?
Wenn wir von
struct C { C(int) {} };
ausgehen. Dann könnte man bei einem op+ als member
1+C() nicht schreiben, wohl aber
C()+1
was doch etwas komisch ist, oder?
wäre der Ctor explicit, könnte man keins der beiden schreiben.
wenn wir nun aber den op+ als non member definieren, kann man
1+C() und C()+1 schreiben, sofern der Ctor nicht explicit ist und keins von beiden, wenn er es nicht ist.dein vektor kann nicht umwandeln, weil er keinen umwandlungskonstruktor hat.
Und nun ekläre mir den vorteil von einem op+ als member.
Umwandlung zählt nicht, weil ich das gerade widerlegt habe.Mein grund warum man ihn non member machen sollte: konsistenz
bool Rückgabe vs Exceptions: Exceptions sollten in krassen Ausnahmefällen zum Einsatz kommen
Nein, bei jeder Art von Ausnahme. Sonst würde sie ja fatalError und nicht Exception heißen.
darum schweigen meine Funktionen nicht darüber, ob sie eine Operation erfolgreich beenden konnten, sondern lassen es den Aufrufer mit true oder false wissen.
Exception schweigen auch nicht, und sind einer C mäßigen fehlerbehandlung technisch überlegen.
Soll ich die technischen Vorteile aufzählen?Andererseits hat dein bool ein Problem: der Call hat keine Ahnung was passiert ist. Das ist doch ein gravierendes Problem, oder etwa nicht?
Wenn wir jetzt davon ausgehen, dass eine Funktion "DisplayScene" welche die ganze Ausgabe auf den Bildschirm rendern sollte einfach false liefert - was macht der caller dann?
Es kann an soviel liegen, lost surface? kein speicher mehr? illegale parameter beim aufruf? sonst was?
er weiß es nicht.
Also wären zumindest integer recht praktisch, damit der caller irgendwie reagieren kann.Wie lautet deine Begründung warum der caller nicht auf fehler reagieren darf? Oder speicherst du dann den fehlercode errno mäßig in einer externen variablen?
Zeiger auf Zeiger: Nicht jeder kommt mit der Zeigerarithmetik zu recht, dabei ist es doch so simpel. Wenn sie Dir aber zu kompliziert sind, dann kannst Du angesichts der heutzutage zur Verfügung stehenden Hauptspeichergrößen natürlich auch jedes Objekt extra im Speicher anlegen.
Verarsch mich bitte nicht.
Ich rede nicht von vector<vector<int> > weil das klar arsch lahm ist.
Ich rede von einem wrappen um diese Zeiger, weil es einfach leichter zu verwenden ist.
Und komm mir nicht mit "ich versteh zeiger arithmetik nicht". Aber vergleiche mal den Aufwand beim Speichermanagement von einem Type** mit dem von einem Wrapper.
Man schreibt diesen Wrapper einmal und hat ihn immer - nie mehr speicherprobleme.
weiters wird der code ausgelagert, es ist doch für deinen Code egal wie du zu dem element [x][y] kommst, also pack es in eine externe Funktion.
Dann kann man die Repräsentation später noch ändern.In C++ gibt es das schöne: zero cost principle
So ein wrapper ist in C++ gratis. er kostet keine performance.Dieser Umgang mit dem Hauptspeicher wäre vor 20 Jahren katastrophal gewesen,
Ich glaube du willst nicht lesen was ich schreibe, oder es versuchen zu verstehen.
Niemand sagt, du sollst ineffizient programmieren. Aber Zeiger auf Zeiger kann man auch anders lösen (ohne dauernd neue Objekt zu erstellen) weil es die Erfindung von Wrappern gibt.
Zeiger sind doch keine Magie. Sie zeigen einfach auf ein Objekt. Diese Objekte brauchen Platz. Logisch. Nur wenn ich die Zeiger schön wrappe, wo sollte da der Speicherverbrauch aufeinmal explodieren?
Klar, wenn man dann einfach immer kopien macht, statt die Objekte nur zu referenzieren hat man probleme - aber wer wäre so dumm so etwas zu tun?
Viele kleine Bausteine verknüpft mit Logik ergeben ein geordnetes Ganzes.
--- ein bekannter Kybernetiker der 60er Jahre_Vordenker der ObjektorientierungEben, genau das behaupte ich ja auch.
Viele kleine Bausteine.
Nicht ein großer, fetter Baustein.Natürlich sind meine Kommentare auch überspitzt, aber ich gehe auf deine Argumente ein. Und drehe dir deine Worte nicht im Mund um (wie du zB bei #define <-> const)
-
besserwisser schrieb:
naja, welche vorteile haben denn symbolische konstanten gegenüber echten? daß man die typsicherheit umgeht oder was? sag mal, da bin ich echt gespannt, was ich alles verpaßt habe, seit ich kein #define mehr verwende...
Konstanten dürfen WIRKLICH nur ein einziges Mal definiert werden, und sind deshalb nicht dafür geeignet, in einem Header zu stehen.
Bei define ist das egal, weil da immer die letzte Definition gilt, die sich natürlich beim selben Header niemals von eventuell vorhergehenden Definitionen unterscheidet.Stell Dir vor, du benötigst den selben Header in den unterschiedlichsten Modulen, dann kannst Du Dir mit Konstanten, die im Header stehen, vom Compiler üblicherweise anhören, dass doppelte Definitionen von Konstanten nicht erlaubt sind, was Konstanten völlig ungeeignet macht, mehreren Modulen zur Verfügung zu stehen. Du musst dann anfangen mit extern und static umständlich Gültigkeitsbereiche zu erfinden, damit das trotzdem geht. Dabei geht es viel einfacher mit #define, da sparst Du dir damit eine Menge Ärger.
Nur ein gut gemeinter Rat.
-
Mecnels schrieb:
Nur ein gut gemeinter Rat.
Der dummerweise völliger Käse ist.
Konstanten dürfen WIRKLICH nur ein einziges Mal definiert werden, und sind deshalb nicht dafür geeignet, in einem Header zu stehen.
Käse. Konstanten können ganz wunderbar in Headern stehen. const heißt in C++ automatisch static, also internal linkage. Desweiteren sind Integerkonstanten in C++ Compile-Zeit-Konstanten, d.h. es wird, sofern du nicht die Adresse der Konstanten bildest, kein Speicher benötigt.
Stell Dir vor, du benötigst den selben Header in den unterschiedlichsten Modulen, dann kannst Du Dir mit Konstanten, die im Header stehen, vom Compiler üblicherweise anhören, dass doppelte Definitionen von Konstanten nicht erlaubt sind, was Konstanten völlig ungeeignet macht, mehreren Modulen zur Verfügung zu stehen
Käse. Wahrscheinlich hast du irgendwann mal geschrieben:
const char* bla = "Fred";
und dann hat sich dein Linker beschwert (zurecht) und du hast die völlig falschen Schlüsse gezogen.
-
HumeSikkins schrieb:
Mecnels schrieb:
Nur ein gut gemeinter Rat.
Der dummerweise völliger Käse ist.
was sogar ich als neuling weiß, weil ichs in meinen paar miniprogrammen, die ich bis jetzt nur erstellt hab, verwendet habe. Mecnels, du hast einfach keine praktische erfahrungen mit sowas. nichts gegen das gedankenexperiment an sich, aber ab und an sollte man seine theorien in der praxis erproben.
ich meine, ich bin durchaus kein anhänger von gruppenzwang, aber wenigstens könntest du doch inzwischen (nachdem sich immer noch niemand FÜR deinen programmierstil ausgesprochen hat) mal auf die idee gekommen sein "hey, ich probier einfach mal aus, was die da die ganze zeit beschreiben". was würdest du schon verlieren? entweder hast du recht, dann kannst du viel aufrichtiger deine meinung weiterhin vertreten, oder wir haben recht, dann kannst du dich verbessern. und ist es nicht dein ziel, dich weiterzuentwickeln, dich und das, was du erschaffst, immer weiter zu verbessern?