Wie non-integral Werte in Klasse am elegantesten initialisieren



  • Ich denke ich werde dann weiterhin einfach in allen Konstruktoren den entsprechenden Wert zuweisen, da ich fast immer weniger als 3 Konstruktoren habe.

    Falls man doch einmal mehr Konstruktoren hat, wie wäre es damit:

    namespace
    {
    	const double X_DEFAULT = 3.23;
    	const double Y_DEFAULT = 3.2343;
    }
    
    class Foo
    {
    private:
    	double x;
    	double y;
    	// Weitere Attribute
    
    public:
    
    	Foo() : x( X_DEFAULT ), y( Y_DEFAULT ) {}
    	// Hier stehen weitere Konstruktoren
    };
    

    Falls sich die default-Werte mal ändern würde, müsste man nur eine einzige Konstante abändern. Overkill? Sinnlos?

    Warum siehst du letzterem skeptisch entgegen?



  • icarus2 schrieb:

    Wiso ist das eigentlich in C++ nicht möglich? Ich habe auch immer Interesse am wiso und nicht nur am wie. Wäre froh, wenn mir da jemand einen Grund liefern könnte.

    Im Gegensatz zu Klassenvariablen (static) sind Membervariablen an eine Instanz, also ein Objekt vom Typ Foo gebunden. Du kannst x,y keine Werte zuweisen bevor ein Foo-Objekt und damit dessen Member erzeugt wurden.

    Du schreibst "Warum geht das in C++ nicht?". Wo geht es denn? Oder meinst du nur die Konstruktor-Delegation wie zB in Java? Wie erwähnt gibts das auch im neuen Standard. Falls du kannst, solltest du den verwenden.



  • icarus2 schrieb:

    Falls sich die default-Werte mal ändern würde, müsste man nur eine einzige Konstante abändern. Overkill? Sinnlos?

    Nein, das ist definitiv ein guter Weg, wenn es sich um bedeutendere Werte handelt als z.B. eine Null für eine Anfangs-Grösse.

    icarus2 schrieb:

    Warum siehst du letzterem skeptisch entgegen?

    Naja, ich sehe jetzt nicht den riesigen Vorteil in der neuen Möglichkeit. Weiterleitende Konstruktoren sollten schon einen Grossteil solcher Fälle abdecken. Ich weiss ich nicht, ob es gut ist, wenn ein paar Member in der Klassendefinition und ein paar im Konstruktor initialisiert werden, weil so Zusammengehöriges getrennt wird. Ausserdem offenbart man weitere Implementierungsdetails im Header, was vor allem wegen der notwendigen Rekompilierung bei einer Wert-Änderung problematisch ist.

    Und nicht zuletzt macht dieses Feature zusammen mit der "einheitlichen" Initialisierung (i.e. 4 verschiedene Möglichkeiten) auf mich den Eindruck, als wolle man "benutzerfreundliche" Features hinzufügen, um es allen recht zu machen. Sehr nachhaltig gedacht ist das meiner Ansicht nach nicht – manchmal wäre weniger mehr.



  • Alles klar. Danke für die Antworten 🙂



  • Santa Klaus schrieb:

    Du schreibst "Warum geht das in C++ nicht?". Wo geht es denn?

    Na in C# zum Bleistift.

    @icarus2:

    Wiso ist das eigentlich in C++ nicht möglich?

    Ich kann nur eine Vermutung anbieten: die Semantik wäre in Verbindung mit einem Copy-Ctor irgendwie ... seltsam.
    Die Initialisierung in der Klassendefinition wäre dann für alle Konstruktoren gültig ausser für den implizit erstellten Copy-Ctor.
    Oder auch für den? Würde der Wert des "anderen" der Kopiert wird dann einfach ignoriert? Oder nachträglich mit operator = zugewiesen?

    Wie macht ihr das in so einer Situation? Wie löst man dieses Problem möglichst elegant, wenn man die Werte nicht einzeln in jedem Konstruktor initialisieren möchte.

    Man beisst in den sauren Apfel und kopiert die Initializer-List. Wenn Werte vorkommen die sich mal ändern könnten, dann kann man (sollte man) Konstanten definieren wie du selbst vorgeschlagen hast.



  • Nexus schrieb:

    [...]Letzterem sehe ich allerdings etwas skeptisch entgegen.

    Warum eigentlich? So kann man sich bei Defaultwerten, die für verschiedene Konstruktoren gleich sein sollen endlich nicht mehr vertun.



  • @hustbear
    Ja stimmt, die Semantik könnte etwas merkwürdig sein. Aber offenbar gehts dann ja mit C++0x.

    @ Tachyon

    Nexus schrieb:

    icarus2 schrieb:

    icarus2 schrieb:
    Warum siehst du letzterem skeptisch entgegen?

    Naja, ich sehe jetzt nicht den riesigen Vorteil in der neuen Möglichkeit. Weiterleitende Konstruktoren sollten schon einen Grossteil solcher Fälle abdecken. Ich weiss ich nicht, ob es gut ist, wenn ein paar Member in der Klassendefinition und ein paar im Konstruktor initialisiert werden, weil so Zusammengehöriges getrennt wird. Ausserdem offenbart man weitere Implementierungsdetails im Header, was vor allem wegen der notwendigen Rekompilierung bei einer Wert-Änderung problematisch ist.



  • icarus2 schrieb:

    ...

    Stimmt, da schreibt er es.
    @Nexus: Beim Weiterleiten muss man aber immer höllisch aufpassen, dass man immer schön für jeden Konstruktor die passende Weiterleitung aufruft. Mit den "In-class member initializers" hat man das Problem nicht mehr.
    Hier mal ein Beispiel...



  • Ja, aber dazu kann man Konstanten im anonymen Namensraum der .cpp-Datei definieren. Dann hat man erstens gleich alle Initialisierungen auf einen Blick und nicht in .cpp- und .hpp-Datei verstückelt. Zweitens ist es auf diese Weise nicht nötig, bei jeder kleinsten Änderung alle Benutzer der Klasse neu zu kompilieren. Das Problem hat man in Java und C# mit ihren Modulsystemen nicht.

    Und hustbaer hat nochmals ein gutes Argument zur Inkonsistenz des neuen Sprachmittels gebracht. Auch das von ihm beschriebene Problem tritt nicht auf, wenn es keine automatisch generierten Kopierkonstruktoren gibt. Man kann halt nicht alles 1:1 nach C++ übertragen.

    Ich weiss nicht, ob ich dieses Feature wirklich verwenden werde.



  • Was meinst du mit Konstanten? Wenn ich in dem Beispiel den Hashalgorithmus immer mit "MD5" initialisieren muss, kann ich das natürlich als Konstante anlegen, muss die ja dann aber trotzdem in allen Konstruktoren übergeben. Die neuen Initializer sparen nicht nur Schreibarbeit, sondern fördern auch die Lesbarkeit, da Member, die in allen Konstruktoren mit den gleichen Werten initialisiert werden sollen, aus den Initialisierungslisten verschwinden können und somit der Fokus mehr auf den Werten liegt, die auch tatsächlich pro Konstruktor anders sind.

    Das Problem mit den Kopierkonstruktoren hat Stroustrup indirekt auch beantwortet. Wenn man einen Member in der Initialisierungsliste initialisiert, überschreibt das einen eventuellen In-Class Initializer. Da der implizite Kopierkonstruktor alle Member mit den jeweiligen Werten von RHS initialisiert, haben da In-Class Initializer also keinen Effekt. Ich sehe da keine Inkonsistenz.

    Mit der getrennten Initialisierung in .cpp und .hpp und bei der erneuten Übersetzung muss ich dir Recht geben, das muss man dann im konkreten Fall abwägen.


Anmelden zum Antworten