Defaultwerte für Instanzattribute



  • Hallo zusammen,

    wenn ich einem als Instanzattribut deklarierten Array eine Defaultbelegung geben will, bevor der Verwender des Objekts über öffentliche Methoden den Array manipuliert: Habe ich dann die Möglichkeit, mit der {...} Notation die Array-Elemente in einer einzigen Anweisung zuzuweisen? Oder muss ich es wie im unten stehenden Code machen?

    Gruss,
    Rüdiger

    #include <cstdlib>
    
    class X {
      public : 
        X();   
    // Irgendwelche Beispielmethoden:
        void wizzle(double* a);          
      private :  
        const static size_t SIZE = 6; 
    // Hier habe ich als nichtstatisches Member einen Array fester Grösse mit double-Werten         
        double _a[SIZE];
    // Eigentlich würde ich gern gleich den Default setzen
    // Aber das ist mir nicht erlaubt, man soll das im Konstruktor machen (warum?)
    //    double _a[SIZE] = {17.,22.,34.,18.,23.,35.};     
      };      
    
    X::X() {
    
    // Also gut, mache ich es also im Konstruktor, wenn es unbedingt so sein soll
    
    // Aber das folgende ist mir leider auch nicht erlaubt
    //  _a = {17.,22.,34.,18.,23.,35.};     
    
    // So muss ich die Defaultbelegung mit hässlichen Einzelwertzuweisungen ausschreiben
    // Muss das wirklich so sein? Im 21. Jahrhundert?
      _a[0] = 17.;
      _a[1] = 22.;
      _a[2] = 34.;    
      _a[3] = 18.;
      _a[4] = 23.;
      _a[5] = 35.;    
      }       
    
    void X::wizzle(double *a ) {
      size_t i = 0;
    // Macht irgendwelche Sachen mit dem Objekt, ändert oder setzt z.B. den Array     
      for (i=0;i<SIZE;i++) _a[i] = a[i];
      }
    


  • Rüdiger Plantiko schrieb:

    // Muss das wirklich so sein? Im 21. Jahrhundert?
      _a[0] = 17.;
      _a[1] = 22.;
      _a[2] = 34.;    
      _a[3] = 18.;
      _a[4] = 23.;
      _a[5] = 35.;
    

    Nein, es gibt da ein paar Alternativen.

    // 1: temporäres Array und kopieren
    X::X()
    {
       double temp[SIZE] = {17.,22.,34.,18.,23.,35.};
       std::copy(temp, temp+SIZE, _a);
    }
    
    // 2: Nicht mühsame und unpraktische Arrays aus C verwenden, sondern
    // moderne Container. std::tr1::array verhält sich bezüglich Speicher und 
    // Geschwindigkeit genau wie ein Array, bietet aber mehr Komfort und
    // Sicherheit im Debug-Modus. Es kann zum Beispiel direkt in der
    // Initialisierungsliste des Konstruktors initialisiert werden.
    #include <array> // evtl. auch <tr1/array>
    
    // Lokale Funktion, die den Initialwert liefert. Durch RVO-
    // Optimierung wahrscheinlich keine unnötige Kopie notwendig
    std::tr1::array<double, SIZE> create_array()
    {
        std::tr1::array<double, SIZE> array = {17.,22.,34.,18.,23.,35.};
        return array;
    }
    
    X::X()
    // _a ist als std::tr1::array<double, SIZE> deklariert
    : _a(create_array())
    {
    }
    
    // 3. Boost und Container verwenden
    #include <boost/assign/list_of.hpp>
    
    X::X()
    // _a ist als std::vector<double> deklariert
    : _a(boost::assign::list_of(17.)(22.)(34.)(18.)(23.)(35.))
    {
    }
    

    Ich weiss, das sind alles mehr Workarounds. Im neuen C++-Standard wird es Initialisierungslisten für benutzerdefinierte Typen geben, ausserdem können dann Arrays im Konstruktor initialisiert werden.

    Allerdings halte ich persönlich eh nicht sehr viel von Arrays und verwende meist std::tr1::array oder die STL-Container. Es kommt bei mir auch nicht sehr häufig vor, dass ich ein Array mit hardgecodeten Zahlen fülle. 😉



  • Hallo Nexus,

    danke für die rasche Antwort zu nachtschlafender Stunde 🙂

    Ich weiss, das sind alles mehr Workarounds.

    Trotzdem danke für das Aufzeigen dieser Alternativen.

    Im neuen C++-Standard wird es Initialisierungslisten für benutzerdefinierte Typen geben, ausserdem können dann Arrays im Konstruktor initialisiert werden.

    Das wäre zu wünschen. Wenn ich ja einen temporären Array mit einem Aggregat-Initialisierer definieren und diesen dann in die gewünschte Membervariable kopieren darf,

    double temp[SIZE] = {1.,2.,3.,4.,5.,6.};
    std::copy(temp, temp+SIZE, _a);
    

    dann spricht schliesslich nichts dagegen, dass der Compiler diese Anweisungen für mich generiert, indem ich nur noch schreiben muss

    _a = {1.,2.,3.,4.,5.,6.};
    

    Alles andere ist unnützes Sprach-Rauschen, das nur die Lesbarkeit des Wesentlichen behindert und daher aus dem Quellcode eliminiert werden sollte.

    Allerdings halte ich persönlich eh nicht sehr viel von Arrays und verwende meist std::tr1::array oder die STL-Container.

    Ich verwende den std::vector, wenn ich Arrays variabler Grösse brauche. Für Arrays fester Grösse bin ich aber bislang mit den "Vanilla" C Arrays vollauf zufrieden.

    Es kommt bei mir auch nicht sehr häufig vor, dass ich ein Array mit hardgecodeten Zahlen fülle. 😉

    Verwendest Du in Deinem Code nie konstante Werte? Ich schon, meistens wenigstens als Defaults. Und manchmal eben auch Listen von konstanten Werten...

    Gruss,
    Rüdiger


  • Administrator

    Rüdiger Plantiko schrieb:

    Verwendest Du in Deinem Code nie konstante Werte? Ich schon, meistens wenigstens als Defaults. Und manchmal eben auch Listen von konstanten Werten...

    Die Frage, welche ich mir da stellen würde:
    Brauchst du wirklich ein Array von konstanter Grösse oder sind es nicht eher (z.B.) 6 unterschiedliche Werte, welche man eigentlich besser in 6 unterschiedliche Variablen aufgeteilt hätte, damit man gleich sprechende Namen für die Werte hat?

    Mir käme nämlich kein aktuelles Beispiel in den Sinn, wo ich sowas benötigt hätte und du redest davon, als wäre dies noch ziemlich üblich für dich 🙂

    Grüssli



  • Hallo Dravere

    Brauchst du wirklich ein Array von konstanter Grösse oder sind es nicht eher (z.B.) 6 unterschiedliche Werte, welche man eigentlich besser in 6 unterschiedliche Variablen aufgeteilt hätte, damit man gleich sprechende Namen für die Werte hat?

    Na, es gibt doch Fälle, wo man eine feste Anzahl von Kästchen (Plätzen, Töpfchen) hat und diese mit Werten belegt haben will, wenn es der User nicht tut. Man kann ja den std::array auch mit Werten vorbelegen. Wenn ich z.B. schreibe

    _a.assign(17.);
    

    dann werden alle Plätze des Arrays mit der Zahl 17 vorbelegt. Jetzt will ich aber nicht lauter gleiche Werte, sondern z.B. die Vorbelegung mit den Werten 3., 2., 1., 3., 2., 1., ... Dieselbe Rechtfertigung, die es für assign() gibt, würde auch für eine solche Initialisierungswunsch gelten.

    Wo Du explizit danach fragst, kann ich Dir mein Beispiel schon sagen: Es ist eine astrologische Anwendung. In der Astrologie wird der Tierkreis nicht nur in die zwölf Tierkreiszeichen, sondern auch, beginnend mit dem Aszendenten, in zwölf ungleich grosse Abschnitte, die sogenannten Häuser, geteilt. Jedem Haus wird eine besondere Wertigkeit (Stärke) zugeordnet. Aber jeder Astrologe hat andere Wahlen für die Wertigkeiten dieser Häuser.

    Daher bietet meine DLL eine API-Funktion an, mit der diese Werte explizit gesetzt werden können. Wenn jedoch keine Werte gesetzt wurden, müssen bestimmte fest verdrahtete Defaultwerte greifen. Und hierzu verwende ich meinen "konstanten" Array, der wegen C++ leider nicht so konstant sein kann, wie ich das gerne hätte.

    http://astropatterns.cvs.sourceforge.net/viewvc/astropatterns/astropatterns/MundanePositions.cpp?revision=1.5&view=markup

    Gruss,
    Rüdiger


Anmelden zum Antworten