[if - else if - else] oder [switch - case - default] ?
-
minastaros schrieb:
hab ich mir
if( 1 == a )angewöhnt, dann meckert der Compiler, wenn man = schreibt...
Wenn ich sowas lese verfalle ich in Blutrausch.
Yoda-Programmieren das ist, Reihenfolge in dieser normaler Mensch denkt keiner.
Also warum verdammt sollte man es dann in der Reihenfolge hinschreiben?
*rage*
-
Besten Dank. Auch wenn wir bereits OT diskutieren, noch kurz die Antwort:
@Nexus: Ich seh es eher als Flüchtigkeitsfehler, kann aus Unachtsamkeit immer mal wieder passieren (wenn auch extrem selten).
Alle Fälle erschlägt man mit dem "Trick" nicht, man minimiert zumindest die Fälle mit konstanten Werten.a == b: Genau da würde die Compilerwarnung ja auch jetzt schon greifen. Ist mir halt noch nicht vorgekommen.
@Volkard: Ich sehe Warnungen immer als Fehler an: Keine Freigabe mit Warnungen. Aber zum Experimentieren kann man sie manchmal tolerieren, d.h. kein Werror.
Die Sache ist einfach so: Das hab ich mir das mal irgendwann angewöhnt, und seit dem ist es so drin. Als eine Lösung für die Bedenken von klöklö weiter vorn im Thread würde es zumindest reichen.Insgesamt und spätestens durch Argument "Hässlichkeit" sehe ich die Sache mal wieder als überdenkenswert an.
Blutrausch ... kein normaler Mensch
Hey, locker bleiben, das steht in durchaus guten Büchern als präventive Maßnahme. Es funktionert und ist besser als gar nichts, aber wie gesagt, ich sehe es absolut ein, dass man durch Warnungen (und dann die "richtige" Reihenfolge der Operanden) wesentlich eleganter zum Ziel kommt, und das heißt: sicherer Code.
*tröst*
-
Lesbarkeit zugunsten der Verminderung von Fehleranfälligkeit derjenigen schlechter zu stellen, die noch nicht geübt genug sind Flüchtigkeitsfehler dieser Art nicht mehr zu machen, lässt den Code automatisch als Anfängercode charakterisieren, auch Mal daran gedacht?
Und wenn man sich das angewöhnt, muss man es sich auch erstmal wieder abgewöhnen, die Mühe sollte man sich sparen.
Wenn man geübter ist, sieht man = statt == sofort, auch binnen Sekunden bei einem 50-100-Zeilen-Quelltext. Also besser daran arbeiten, dass man auf diesen Stand kommt. Und wenn man sich vertut, hilft einem Mr. Compiler weiter.

-
minastaros schrieb:
Hey, locker bleiben, das steht in durchaus guten Büchern als präventive Maßnahme.
Die guten C++-Bücher sind alle so modern, daß sie von ordentlichen Compilern ausgehen.
-
Eisflamme schrieb:
Wenn man geübter ist, sieht man = statt == sofort,...
"Defensives Programmieren" geht ja gerade davon aus, dass man auch als Erfahrener eben trotzdem mal was übersehen kann, und sei es ein einziges Mal unter tausenden, und Maßnahmen einzuführen, die das von vorne herein verhindern. Jeder lernt unterschiedlich, man entwickelt sich weiter, und ich z.B. habe meine "Erfahrung" durch den Compilerhinweis heute erweitert.
Übrigens fand ich bislang die Lesbarkeit eines "1 == a" weit weniger störend und den Nutzen größer als z.B. dasZusammenschreibenvonmathematischenAusdrücken, was man so oft sieht.volkard schrieb:
Die guten C++-Bücher sind alle so modern, daß sie von ordentlichen Compilern ausgehen
Das, was ich konkret meine ("Code complete"), sieht Defensives Programmieren zunächst mal sprachunabhängig und diskutiert - in mehreren Programmiersprachen und oder sogar nur Pseudocode - einen ganzen Haufen möglicher Maßnahmen. Vom Compiler ist auf dieser Ebene noch gar nicht die Rede (wie gesagt, ich stimme dem ansonsten ja voll zu).
Ich denke, wir sollten nicht weiter über ein = streiten.
-
minastaros schrieb:
Eisflamme schrieb:
Wenn man geübter ist, sieht man = statt == sofort,...
"Defensives Programmieren" geht ja gerade davon aus, dass man auch als Erfahrener eben trotzdem mal was übersehen kann, und sei es ein einziges Mal unter tausenden, und Maßnahmen einzuführen, die das von vorne herein verhindern. Jeder lernt unterschiedlich, man entwickelt sich weiter, und ich z.B. habe meine "Erfahrung" durch den Compilerhinweis heute erweitert.
Übrigens fand ich bislang die Lesbarkeit eines "1 == a" weit weniger störend und den Nutzen größer als z.B. dasZusammenschreibenvonmathematischenAusdrücken, was man so oft sieht.Du musst den Nutzen von 1 == a mit der verschlechterten Lesbarkeit vergleichen, ich finde das rechnet sich nicht.
Und ich weiß nicht, was Du mit dem Zusammenschreiben von mathematischen Ausdrücken meinst, Du schreibst ja gerade einen Namen auf? Und falls Du lange aber sprechende Funktionsnamen meinst, kann ich entgegnen, dass:
- , wenn die Länge nötig ist, um es sprechend zu schreiben, es für das Verständnis des Codes durch sprechende Namen auch wert ist
- Du es nicht richtig gemacht hast, weil von und mathematischen auch groß geschrieben sein müsste

-
minastaros schrieb:
"Defensives Programmieren" geht ja gerade davon aus, dass man auch als Erfahrener eben trotzdem mal was übersehen kann, und sei es ein einziges Mal unter tausenden, und Maßnahmen einzuführen, die das von vorne herein verhindern.
So kommen wir nicht weiter. Dann müsste man z.B. auch den Code mit irrelevanten
const-Schlüsselwörtern fluten, um zwar die Fehleranfälligkeit einzuschränken, mit ihr aber gleich die Benutzbarkeit, Übersichtlichkeit und Flexibilität. Man dürfte keinenamespace detailmehr verwenden, weil man ja aus Versehen darauf zugreifen könnte. Immer NVI einsetzen, damit man unabsichtlich qualifizierte Aufrufe verhindert. Selbst bei internen Klassen immer allesprivatemachen und mit etlichenfriends oder Zugriffsfunktionen verzieren. Klassen müsste man immer gleich mit virtuellen Destruktoren ausstatten für den Fall, dass sie eines fernen Tages polymorph und mitnewbenutzt werden.Fakt ist, dass du in C++ vieles nicht 100% sicher hinbringst. Die Versuche, es dennoch zu tun, führen selten zum Ziel, aber bringen ungerechtfertigt viele Nachteile und Schein-Sicherheiten mit sich. Das ist ein zu hoher Preis, um einen nie auftretenden Fehler teilweise zu verhindern.
-
Nexus schrieb:
Dann müsste man z.B. auch den Code mit irrelevanten
const-Schlüsselwörtern fluten, um zwar die Fehleranfälligkeit einzuschränken, mit ihr aber gleich die Benutzbarkeit, Übersichtlichkeit und Flexibilität.Definiere irrelevant. Ich schreib so oft
const, ich könnte da ne eigene Taste dafür brauchen. Finde ich auch gut so.Man dürfte keine
namespace detailmehr verwenden, weil man ja aus Versehen darauf zugreifen könnte.Ne, aus versehen greift man nicht auf Detail-Namespaces zu. Also gut, es kann Unfälle mit ADL geben. Aber auch nur wenn man z.B. Basisklassen für "nicht-detail" Typen in den Detail-Namespace steckt. Wo sie IMO nix verloren haben, sobald sie zum Interface von "nicht-detail" Typen gehören.
Immer NVI einsetzen, damit man unabsichtlich qualifizierte Aufrufe verhindert.
Ja, NVI kann lästig werden. Führt zu deutlich mehr Code als wenn man einfach nach dem Motto "muss man halt wissen was man tut" programmiert. Hat aber auch Vorteile.
Selbst bei internen Klassen immer alles
privatemachen und mit etlichenfriends oder Zugriffsfunktionen verzieren.Mach ich bei fast allen internen Klassen. Also alle die mehr sind als eine Sammlung von Variablen + Konstruktor. Sobald etwas Memberfunktionen hat wird es zugemacht.
Klassen müsste man immer gleich mit virtuellen Destruktoren ausstatten für den Fall, dass sie eines fernen Tages polymorph und mit
newbenutzt werden.Er. Klassen immer gleich mit virtuellem Dtor machen wäre IMO sogar kontraproduktiv. Ein virtueller Dtor impliziert für mich nämlich die Aussage "diese Klasse ist als Basisklasse ausgelegt" bzw. "es ist OK und kann Sinn machen von dieser Klasse abzuleiten". Was oft falsch ist. Und Code der falsche Dinge behauptet (wenn auch nur implizit), verhindert sicher keine Fehler.
Fakt ist, dass du in C++ vieles nicht 100% sicher hinbringst. Die Versuche, es dennoch zu tun, führen selten zum Ziel, aber bringen ungerechtfertigt viele Nachteile und Schein-Sicherheiten mit sich.
Äh.
Was sind denn die Nachteile an const-korrektem Code oder Kapselung von internen Klassen?
Ausser dass man dadurch minimal länger braucht den Code ursprünglich zu schreiben fällt mir jetzt kein Grund ein.Die Wartbarkeit wird dadurch aber gesteigert, da es ja nicht nur ein paar unabsichtliche Fehler verhindert, sondern auch das Verstehen des Code vereinfacht. Das rechnet sich sehr schnell.
constsagt mir sofort was wenn ich es sehe, nämlich dass hier etwas nur gelesen wird, bzw. nur zum Lesen weitergereicht/entgegengenommen etc.Kapselung von internen Klassen ist auch gut, da es den Code in kleinere Stücke unterteilt. Wenn ich Code lesen muss, und da eine "offene" interne Klasse sehe die drei Memberfunktionen hat, dann sagt mir das nicht viel. Es könnte 1000 Stellen geben wo die Member modifiziert werden. Vielleicht war der Author des Codes so nett und hat das nicht gemacht, aber ich sehe das nicht auf den 1. Blick.
Wenn da allerdings "private:" steht, dann weiss ich dass ich hier eine schön gekapselte Klasse betrachte, und die 3 Memberfunktionen alles sind was ich mir angucken muss, um diesen Teil zu verstehen. Der Code zerfällt in kleinere Stücke, und wird dadurch schneller verständlich.Ich sehe hier auch einen wesentlichen Unterschied zu "if (1 == var)": Die "if (1 == var)" Schreibweise sagt mir nichts was mir "if (var == 1)" nicht auch sagt.
const-korrekter Code und Kapselung erzählen mir dagegen etwas, und zwar über andere Stellen des Programms. Das macht IMO vieles einfacher.
Ich musste schon oft genug Code lesen und/oder bearbeiten wo diese Dinge (const-Korrekter Code etc.) nicht gemacht wurden, und es war jedes mal grauenhaft. Code der diese und andere Dinge macht um "lesbar" zu sein finde ich dagegen viel viel angenehmer zu lesen. Auch wenn es dadurch ein paar Zeilen mehr werden.BTW: Weil die Bezeichnung "defensiv programmieren" in diesem Thread genannt wurde: ich würde das auch nicht als "defensiv programmieren" bezeichnen, sondern einfach als strukturiert programmieren.
-
Ich weiss nicht, ob du meinen Beitrag richtig verstanden hast. Natürlich sind die genannten Sicherheitsmechanismen gut, aber ihr übermässiger Einsatz ist es nicht. Du solltest mich doch genug gut kennen, um zu wissen, dass ich
const,privateetc. nicht generell meide
Zum virtuellen Destruktor bin ich genau deiner Meinung, ich hab mich hier sogar schon einige Male gegen automatisch virtuelle Destruktoren bei Vererbung ausgedrückt (weil halt Vererbung nicht immer Polymorphie mit sich bringt). Und mich dabei ebenfalls darauf bezogen, dass man mit dem Vorhandensein von virtuellen Destruktoren vieles über die Semantik der Klasse aussagt.
Bei zu viel
constist das Problem einerseits in der Schnittstelle (folgender Code), andererseits kann man durch unüberlegten Einsatz die Flexibilität von Objekten stark einschränken. Einconst-Member nimmt der Klasse z.B. ihre Wertsemantik und verunmöglicht ein Speichern in STL-Containern, zumindest in C++98. Das hat auch nichts mehr mit Const-Correctness zu tun.void Car::SetSpeed(const float speed); const float Car::GetSpeed() const;Kapselung genau gleich. Natürlich verwende ich sie auch intern. Aber wenn etwas nur Daten bündelt oder sehr lokal ist (z.B. Funktor), verzichte ich darauf, selbst wenn man dadurch potenziell Fehler einbauen könnte.
-
Ich weiss nicht ob du deinen Beitrag verständlich geschrieben hast

Ja, was top-level const bei Parametern angeht bin ich ganz deiner Meinung.
Was const bei Membern angeht: die meisten meiner Klassen sind keine Werte-Klassen, und daher explizit noncopyable (über privates Ableiten von boost::noncopyable).
In denen mach' ich dann auch "const as const can" bei Membern. z.T. schreibe ich eigene Hilfsfunktionen zum Initialisieren, nur damit ich ein paar Member mehr const machen kann
Manche Leute überlegen sich bei C++ auch bei jeder Klasse wie sie diese kopierbar und zuweisbar machen können, weil das ja der "C++ way of doing it" sei. Das finde ich nicht gut. Manche überspringen den Schritt auch sich was zu überlegen, und schreiben Klassen die zwar zuweisbar sind (weil zufällig nichts vorkommt was das Generieren des Assignment-Operators verhindert), wo es dann aber knallt wenn man es macht. Das finde ich noch schlechter

Ich mache keins von beiden. Wenn etwas für mich nicht sofort nach "reine Value-Semantik" riecht, dann mache ich es erstmal noncopyable (und das bedeutet meist auch "partiell-immutable"). Wenn sich im weiteren Verlauf herausstellt dass das doof war, dann wird halt refactored. Kein Beinbruch.
Auch gibt es Fälle wo etwas zwar vom rein logischen her Value-Semantik hat (haben sollte), aber es
* aus bestimmten Gründen (*) schlecht wäre es so zu implementieren und
* es auch nicht schadet wenn man die Klasse ganz oder teilweise immutable macht, da die Funktionalität einfach nirgends gebraucht wird.In den Fällen mache ich die Dinger dann auch "immutable". Und dann bekommen die Member auch alle const (sofern es mit vertretbarem Aufwand möglich ist).
(*) Gründe dafür die mir häufiger begegnen:
- es ist einfach (zu) viel Aufwand korrekte Zuweisungssemantik zu implementieren
- es verhindert (zu) viele Optimierungen in der Klasse selbst
- es verhindert (zu) viele Optimierungen in anderen Klassen
DAS Beispiel aus der SCL das mir bei dem Thema immer einfällt ist
std::string. IMO wäre einiges einfacher, und auch einfacher performant zu implementieren, wenn es da die von Java bekannte Teilung String/StringBuilder gäbe (wobei der String dann immutable ist).