objekt als klassenkonstante - geht das?



  • kann man Objekte als Klassenkonstanten festlegen? Also analog zu "static const int=12;" im Header.
    Zwei Varianten kenne ich - gefallen mir beide nicht:
    1. Das klassenkonstante Objekt einfach im Konstruktor erzeugen bzw. initialisieren.

    2. eine static const Funktion die das Objekt in einer static Variablen hält und beim ersten Aufruf es initialisiert und zurückgibt.

    Kann man das ganze nicht direkter machen? Also das man in der Deklaration der Klasse sieht, das sie eine Konstante(ein Klasse) mit folgendem Wert(ein Objekt) hat?

    Geht das ganze eventuell nur mit bestimmten Typen/Klassen?


  • Mod

    Eigene Klassen verhalten sich genauso wie die eingebauten Datentypen. Du kannst alles ganz genauso machen wie du es von diesen her kennst.



  • dermoritz schrieb:

    Zwei Varianten kenne ich - gefallen mir beide nicht:
    1. Das klassenkonstante Objekt einfach im Konstruktor erzeugen bzw. initialisieren.

    Woher kennst du die Variante? Das funktioniert nämlich nicht. Statische Objekte werden bereits vor der ersten Instanz der Klasse erzeugt und initialisiert, und zwar einmal. Im Konstruktor kannst du es also weder erzeugen (da es schon existiert) noch verändern (da es const ist).

    Kann man das ganze nicht direkter machen? Also das man in der Deklaration der Klasse sieht, das sie eine Konstante(ein Klasse) mit folgendem Wert(ein Objekt) hat?

    Geht das ganze eventuell nur mit bestimmten Typen/Klassen?

    Klares jein:
    - Du kannst konstante statische Member jeden Typs haben.
    - Du kannst nur Integrale Typen direkt mit einem Wert belegen.
    - Alle anderen Typen müssen einmalig (in einer einzigen Übersetzungseinheit, also nicht im header) definiert und initialisiert werden.

    //X.h
    class Foo { /* ... */ };
    
    class X
    {
      const static int i = 15; //ok
      //const static Foo f = xyz; //FEHLER, nur integrale Typen mit Initialisierung hier
      const static Foo f; //ok, Deklaration
    };
    
    //X.cpp
    const Foo X::f = createSomeFoo(); //Definition und Initialisierung
    


  • SeppJ schrieb:

    Eigene Klassen verhalten sich genauso wie die eingebauten Datentypen. Du kannst alles ganz genauso machen wie du es von diesen her kennst.

    hmmmmmmm ... ich glaube, da irrst Du.
    Es gibt einige wenige Ausnahmen, bei denen sich primitive Typen von anderen unterscheiden(z.B. dass man von primitiven Typen nicht ableiten kann, dass bestimmte primitive Typen als template-parameter infrage kommen (andere Typen nicht), ...)
    - und dieser Fall gehört IIRC dazu.

    Nur Typen, die zur Compilezeit initialisiert werden, können bereits bei der Deklaration initialisiert werden.

    Gruß,

    Simon2.



  • Simon2 schrieb:

    Nur Typen, die zur Compilezeit initialisiert werden, können bereits bei der Deklaration initialisiert werden.

    Auch nicht alle, der Standard ist da sehr restriktiv:

    ISO/IEC 14882, §9.4.2/4 schrieb:

    4 If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear in integral constant expressions within its scope. The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.



  • danke, das hatte ich befürchtet.
    (@SeppJ - in einer konsequent objektorientierten Sprache trifft das zu, offensichtlich gilt das aber nicht für C++.)

    @pumuckl deine Variante benötigt eine "createSomeFoo()" Funktion, die ja auch irgendwo deklariert/implementiert sein muss,oder?

    In Meinem Fall handelt es sich um eine QMap (http://doc.trolltech.com/4.5/qmap.html) und das Objekt hat 2 Einträge. Meine createSomeFoo() Funktion sähe dann z.B. so aus?:
    header:
    ...

    const static QMap<String,int> map
    

    ...
    implementierung

    ...
    X::createSomeFoo(){
     map["one"] = 1;
     map["three"] = 3;
    }
    

    Die Frage ist ob das geht? Ich ändere ja das Objekt 2mal - mit jeder Zuweisung?!



  • dermoritz schrieb:

    @pumuckl deine Variante benötigt eine "createSomeFoo() Funktion, die ja auch irgendwo deklariert/implementiert sein muss,oder?"

    Es muss ein Konstruktoraufruf sein, ginge also auch ohne extra Funktionen. In meinem Beispiel würde durch createSomeFoo() ein Foo erstellt, ggf. noch verändert und dann an den (Copy-)Konstruktor des statischen Memberobjekts übergeben. In deinem Fall müsste es was ähnliches sein, da es ja keinen direkten Map-Konstruktor geben wird, der genau die zwei Belegungen macht.

    class X
    { 
     const static QMap<String,int> map; //Deklaration
    
    private:
     static QMap<String,int> mapInit(); //Hilfsfunktion
    };
    
    //X.cpp
    QMap<String,int> X::mapInit()
    {
      QMap<String,int> tmp;
      tmp["one"] = 1;
      tmp["three"] = 3;
      return tmp;
    }
    
    const QMap<String,int> X::map(X::mapInit()); //oder mit =, Hauptsache QMap hat einen Copy-Ctor
    

    edit: Du kannst natürlich auch die Memberfunktion mapInit weglassen und einfach als Funktion lokal in deiner .cpp definieren. Sie wird ja nur dort gebraucht um einmalig im Programm die statische Konstante zusammenzuschrauben.



  • erstmal: danke pumuckl.

    aber für einen c++ noob ergeben sich weitere fragen:

    ich würde die Variante bevorzugen, die mapInit nicht im header erwähnt - dafür muss ich den Eintrag ( static QMap<String,int> mapInit();) im Header einfach weglassen und in der cpp noch static davorschreiben, oder?

    und

    const QMap<String,int> X::map(X::mapInit());
    

    kann ich irgendwo (z.b. ganz oben) in die cpp schreiben? Dieses Objekt wird dann genau einmal für alle Instanzen von X angelegt - automatisch(wann genau?)?



  • dermoritz schrieb:

    ich würde die Variante bevorzugen, die mapInit nicht im header erwähnt - dafür muss ich den Eintrag ( static QMap<String,int> mapInit();) im Header einfach weglassen und in der cpp noch static davorschreiben, oder?

    Du müsstest außerdem die Qualifizierung als Klassenmethode weglassen, sowohl bei der Definition als auch beim Aufruf. Das static kannst du machen, musst du aber nicht. Meines Wissens hieße das nur, dass die ÜE dann kein entsprechendes Linkersymbol enthielte. Du kannst die Funktion auch in einen anonymen Namespace packen, oder sie ebene nur in der cpp ganz normal definieren.

    //X.cpp
    
    //namespace { //Variante 1
    //static //Variante 2
    
    QMap<String,int> mapInit()
    {
      QMap<String,int> tmp;
      tmp["one"] = 1;
      tmp["three"] = 3;
      return tmp;
    }
    
    //} //end namespace
    
    const QMap<String,int> X::map(mapInit());
    


  • unabhängig davon wie ich die init-Funktion definiere wann und wie wird dieses

    const QMap<String,int> X::map(mapInit());
    

    ausgeführt?

    um die Frage mal zu konkretisieren:

    da die map staic const ist, kann ich davon ausgehen, das mapInit nur einmal ausgeführt wird und nicht einmal für jede Instanz der Klasse? Oder wird es bei jeder Instanziierung ausgeführt nur läuft dann die Zuweisung ins leere?
    Für letzteren Fall könnte man eine Abfrage in die Initfunktion machen, die überprüft ob map schon befüllt ist. Falls ja wird map zurückgegeben ansonsten eben initialisiert?!



  • dermoritz schrieb:

    ...(@SeppJ - in einer konsequent objektorientierten Sprache trifft das zu, offensichtlich gilt das aber nicht für C++.)

    Schon eher als auf Java. 😉
    Ich vermute, die Zahl der in dieser Hinsicht "konsequent objektorientierten Sprachen" mit nennenswerter Praxisverbreitung ist seeeeehr klein.

    Gruß,

    Simon2.



  • pumuckl schrieb:

    Simon2 schrieb:

    Nur Typen, die zur Compilezeit initialisiert werden, können bereits bei der Deklaration initialisiert werden.

    Auch nicht alle, ...

    Das war mir durchaus bewusst, aber es ging mir um die Abgrenzung "nach oben" (also die Aussage, dass Typen, die zur Laufzeit initialisiert werden, nicht infrage kommen).

    Aber danke für die Ergänzung.

    Gruß,

    Simon2.



  • dermoritz schrieb:

    unabhängig davon wie ich die init-Funktion definiere wann und wie wird dieses

    const QMap<String,int> X::map(mapInit());
    

    ausgeführt?

    IIRC garantiert der Standard nur, dass X::map initialisiert wird, bevor eine Funktion aus der selben TU aufgerufen wird. Der genaue Wortlaut ist sicher ein anderer, und es gibt einige Fallstricke:

    http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12

    Die meisten Compiler machen es aber so, dass alle statischen/globalen Objekte initialisiert werden, bevor main() losläuft.


Anmelden zum Antworten