Mehrere Konstruktoren vs default Argumente
-
Hallo zusammen,
ich habe gestern sowas in der Art gelesen: (gekürzt)
class complex { double re, im; public: complex(double r, double i) :re{r}, im{i} {} complex(double r) :re{r}, im{0} {} complex() :re{0}, im{0} {}
Ich hab mich irgendwie gefragt, wieso man da nicht einfach schreibt
class complex { double re, im; public: complex(double r = 0, double i = 0) :re{r}, im{i} {}
Gibt es da einen design-technischen Unterschied, bzw. : Was würde man in einem ordentlichen Program eher machen? Ich meine, sind immerhin drei Zeilen in einer zusammengefasst, oder übersehe ich da was?
Mag vielleicht ne ziemlich "pingelige" Frage sein, aber sie ist mir eben gekommen und daher stell ich sie halt mal..
LG
HarteWare
-
Default-Argumente sind ein Sprachmittel, dessen Sinn sich mir gar nicht erschließt. Mit denen bin ich nie warm geworden.
Technisch ist der Unterschied, daß bei Default-Argumenten es nur eine Funktion gibt und im Code des Aufrufers der Compiler automatisch die Fehlenden Sachen reinstopft, während bei Überladfung der Aufrufer unbehelligt bleibt und es drei Funktionen gibt.
-
Hallo,
vielen Dank für deine Antwort.
Ich werds dann wohl ein Zukunft einfach so machen, wie ich es auch in meinem Buch gelesen habe, also ohne default Argumente...LG
-
Der Vorteil bei Default-Parametern ist neben kürzerem Code eine aussagekräftigere Schnittstelle: Der Aufrufer sieht bei
complex(double r = 0, double i = 0);
wie Real- und Imaginärteil beim Aufruf ohne Argumente initialisiert werden, bei
complex()
muss er dazu im Allgemeinen die Dokumentation lesen (die Initialwerte sind nicht immer so offensichtlich wie bei komplexen Zahlen). Der Benutzer braucht bei überladenen Funktionen also länger, um deren Relation zu erkennen.
-
na toll, jetzt bin ich wieder unstimmig
Trotzdem natürlich danke für deine Antwort, ich höre gerne verschiedene Quellen und bilde dann meine eigene Meinung darausLG
-
Wenn du dich entschieden hast, dass du so ein Interface mit 3 Überladungen bereitstellen willst, ist die Wahl zwischen Default-Parametern und Überladungen mehr oder weniger Geschmackssache (kann bei nicht-inline-Funktionen einen Unterschied machen, aber das ist hier nicht der Fall).
Der Grund, weshalb viele (mich eingeschlossen) den Default-Parametern kritisch gegenüber stehen ist, dass es oft eine schlechte Idee ist, diese zu verwenden. Eine Funktion sollte genau eine Aufgabe haben und sobald Default-Parameter im Spiel sind, hat sie meistens mehrere Aufgaben, was bedeutet, dass man sie lieber in zwei Funktionen aufteilt und diese anders benennt. Weil man leicht in die Versuchung kommt, Default-Parameter zu übernutzen, ist es für Anfänger besser, diese generell zu vermeiden.
Wenige Ausnahmen, wo Default-Parameter vertretbar sind, sind Konstruktoren (selbst std::vector hat tausend Konstruktoren mit unterschiedlichsten Aufgaben -- was seit C++11 mit der uniform initialization zu Problemen führt!). Aber auch da sind named constructors oft eine bessere Lösung. Konstruktoren von fractional und complex sind so fast die einzigen Fälle wo default-Parameter Vorteile haben.
Aus Einheitlichkeit würde ich aber generell von Default-Parametern Abstand nehmen und hier keine nehmen.
-
quelloffen schrieb:
Der Grund, weshalb viele (mich eingeschlossen) den Default-Parametern kritisch gegenüber stehen ist, dass es oft eine schlechte Idee ist, diese zu verwenden. Eine Funktion sollte genau eine Aufgabe haben und sobald Default-Parameter im Spiel sind, hat sie meistens mehrere Aufgaben, was bedeutet, dass man sie lieber in zwei Funktionen aufteilt und diese anders benennt. Weil man leicht in die Versuchung kommt, Default-Parameter zu übernutzen, ist es für Anfänger besser, diese generell zu vermeiden.
Die Begründung finde ich nicht überzeugend. Erstens sehe ich den Zusammenhang zwischen optional angegebenen Argumenten und zu vielen Aufgaben nicht -- es kann gut sein, dass eine Aufgabe bestimmte Parameter erfordert, die aber in den meisten Situationen nicht explizit angegeben werden müssen. Konsequenterweise müsstest du sonst auch gegen Default-Template-Parameter sein.
Zweitens, selbst wenn die Version mit Default-Parametern dazu tendiert, zu viele Aufgaben zu vereinen, dann gilt das Gleiche für überladene Funktionen, welche Aufrufe lediglich weiterleiten. Aus der Sicht des Aufrufers (welcher die Aufgaben anordnet) gibt es überhaupt keinen Unterschied. Und das Problem der zu vielen Aufgaben ist sowieso nicht gelöst, da eine Funktion immer noch alles tut, aber es eben noch zusätzliche Funktionen gibt, die (ausser der Weiterleitung) nichts tun.
Wo Default-Parameter verwirrend sein können, ist bei virtuellen Funktionen, zumal der C++-Standard die Überschreibung mit verschiedenen Default-Werten erlaubt. In Fällen, wo die Implementierung bei Angabe des Defaultparameters eine komplett andere ist, wäre Überladung wohl auch die bessere Wahl. Aber ich sehe keinen Sinn darin, kategorisch mehr Boilerplate-Code wie in Java (lediglich weiterleitende Funktionen) zu schreiben, nur um Default-Parameter zu meiden.