Statische Klassenkonstante von Basisklasse in Subklasse verwenden



  • Naja - schlechtes design - das ganze soll ein scanner für einen parser werden; und da muss ich halt abhängig des gelesenen zeichens ein entsprechendes token generieren.
    wie meinst du es mit delegates?

    lg, MrGentleman



  • Compiletime-Konstanten müssen in der Klasse initialisiert werden, sonst werden es Laufzeitkonstanten 😉



  • ~john schrieb:

    ...Switch in OOP ist fast immer ein Designfehler, ...

    Naja, das riecht jedenfalls nach heftigster Verallgemeinerung. 😉

    Es stimmt natürlich, dass switch/case ein Konstrukt zur Ablaufsteuerung ist ... aber ganz überflüssig ist es nicht (und sei es nur, um aufgrund vom Input die richtige Klasse zu instantiieren).

    Gruß,

    Simon2.



  • LordJaxom schrieb:

    Compiletime-Konstanten müssen in der Klasse initialisiert werden, sonst werden es Laufzeitkonstanten 😉

    Mann oh Mann.....das war's, herzlichen Dank!

    Ein switch/case bei einer scanner-implementierung ist meines wissens eigentlich unumgänglich, da dieser ja einfach eine große zustandsmaschine ist (es sei denn, sie wird mittels funktionspointer implementiert) und einfach abhängig vom gelesenen zeichen ein entsprechendes token für den parser generiert;
    Kurzer Auszug:

    switch (ch) {
      case Scanner::_EOF:
        _token->type = Token::_EOF;
    	break;
    
      case MyConcreteScanner::QUOTE:
        _token->type = Token::QUOTE;
        nextChar();
    	break;
    

    lg, MrGentleman



  • MrGentleman schrieb:

    Ein switch/case bei einer scanner-implementierung ist meines wissens eigentlich unumgänglich, da dieser ja einfach eine große zustandsmaschine ist (es sei denn, sie wird mittels funktionspointer implementiert)

    Na, es gibt unzählige Arten, die zu implementieren, z.B. als Graph, per State Pattern oder Lookup-Tabelle. Aber in Deinem Fall ist wahrscheinlich die Switch-Implementierung wirklich die schnellste (wenn der Compiler dafür intern eine Lookup-Tabelle generiert).



  • LordJaxom schrieb:

    Compiletime-Konstanten müssen in der Klasse initialisiert werden, sonst werden es Laufzeitkonstanten 😉

    Nicht wenn man sie außerhalb mit einem Rvalue initialisiert 😉


  • Mod

    KasF schrieb:

    LordJaxom schrieb:

    Compiletime-Konstanten müssen in der Klasse initialisiert werden, sonst werden es Laufzeitkonstanten 😉

    Nicht wenn man sie außerhalb mit einem Rvalue initialisiert 😉

    womit willst du sonst initialisieren?
    Eine Initialisierung außerhalb der Klasse ist eine Definition, das würde unweigerlich zu Mehrfachdefinition führen, wenn man es im Header durchführt. Das darf man sich nur bei Template-Membern leisten.



  • camper schrieb:

    womit willst du sonst initialisieren?

    Mit einem Lvalue, zB einer Funktionsrückgabe.

    int f() { return 3; }
    
    class Base
    {
        public:
                    static unsigned const int VAR;
    };
    
    unsigned int const Base::VAR = f();
    

    Hier wird VAR mit einem nicht modifizierbaren Lvalue initalisiert.
    Somit wäre es doch eine Laufzeitkonstante und in einem Ausdruck, wo eine Compiletimekonstante erwartet wird, ungültig ?
    Diese Initalisierung funktioniert doch auch nur außerhalb ?

    Compiletimekonsante:

    int f() { return 3; }
    
    class Base
    {
        public:
                    static unsigned const int VAR;
    };
    
    unsigned int const Base::VAR = 3;
    

    Hier wäre es nun eine Compiletimekonstante und für das switch geeignet und auch außerhalb initalisiert.
    ->

    LordJaxom schrieb:

    Compiletime-Konstanten müssen in der Klasse initialisiert werden, sonst werden es Laufzeitkonstanten 😉

    Das meinte ich eigentlich 🙂


  • Mod

    KasF schrieb:

    camper schrieb:

    womit willst du sonst initialisieren?

    Mit einem Lvalue, zB einer Funktionsrückgabe.

    Hier werden Begriffe falsch verwendet. Ein (skalares) Lvalue ist ein Ausdruck der auf ein (skalares) Objekt verweist, während ein Rvalue dieses Typs einen bestimmten Zustand oder Wert repräsentiert, den ein solches Objekt annehmen könnte. Das Ergebnis eines Funktionsaufrufes ist ein Rvalue des Rückgabetyps (es sei denn, dieser Typ ist eine Referenz - dann ist der entsprechende Aufruf, wie in jedem analogen Kontext, ein Lvalue des Typs, auf den die Referenz verweist).
    Die entscheidende Frage ist hier, ob die Konstante mit einem konstanten Ausdruck initialisiert wurde.
    Ein Primärausdruck, der auf eine konstante Variable verweist, ist genau dann ein konstanter Ausdruck, wenn diese konstante Variable (lexikalisch) zuvor mit einem konstanten Ausdruck initialisiert wurde. Dabei spielt es wiederum keine Rolle, ob diese Initialisierung in der Klasse oder außerhalb stattgefunden hat. dass wir sie in Fällen wie diesem nicht außerhalb machen, hat andere Gründe - ODR.

    struct Foo
    {
        static const int x = 1;
        static const int y;
    };
    
    int foo = Foo::x; // Foo::x ist konstanter Ausdruck -> statische Initialisierung
                      // Foo::x muss trotzdem noch definiert werden, denn dies ist kein Kontext, in dem ein konstanter Ausdruck stehen muss
    const int Foo::x;
    int bar = Foo::y; // Foo::y ist kein konstanter Ausdruck -> dynamische Initialisierung
                      // Foo::y kann hier nicht als case-Label benutzt werden
    
    const int Foo::y = 2; // 2 ist als Literal (natürlich) ein konstanter Ausdruck
    
    int baz = Foo::y; // Foo::y ist konstanter Ausdruck -> statische Initialisierung
                      // Foo::y könnte hier als case-Label benutzt werden
    


  • camper schrieb:

    Hier werden Begriffe falsch verwendet. Ein (skalares) Lvalue ist ein Ausdruck der auf ein (skalares) Objekt verweist, während ein Rvalue dieses Typs einen bestimmten Zustand oder Wert repräsentiert, den ein solches Objekt annehmen könnte. Das Ergebnis eines Funktionsaufrufes ist ein Rvalue des Rückgabetyps

    Das habe ich auch bis dato immer so gedacht, bis ich beim Comeau Compiler und in diverser Literatur darauf gestoßen bin, dass es modifiziberbare Lvalues und nicht modifizierbare Lvalues gibt.

    Beispiel:

    const int t  = 4; 
    t = 33;               // Error, t ist ein nicht modifizierbarer Lvalue ?
    f() = 4;              // Error, Rückgabe ist ein Rvalue ?
    4 = 3;                // Error, 4 ist Rvalue ? 
    4 + 3 = 2;           // the same ?
    

    Richtig so ?

    Ich habe das mal ausprobiert und Comeau sagt überal nicht modifizierbarer Lvalue. Das Wort Rvalue ist gar nicht aufgetaucht.

    Klarheit bitte ? 🙂


  • Mod

    KasF schrieb:

    Ich habe das mal ausprobiert und Comeau sagt überal nicht modifizierbarer Lvalue.

    Da steht

    expression must be a modifiable lvalue

    - mit anderen Worten, der fehlerhafte Ausdruck ist kein modifizierbarer Lvalue. Da steht nicht, ob der Ausdruck zwar ein Lvalue sei, aber nicht modifizierbar (trifft in erste Zeile zu), oder schlicht kein Lvalue ist (die anderen 3 Fälle).
    Skalare Rvalues sind niemals modifizierbar (weil sie keine Objekte repräsentieren) - tatsächlich geht ja jede cv-Qualifikation verloren, wenn man den Wert eines Objektes liest (=den Lvalue-Ausdruck in einen Rvalue-Ausdruck konvertiert).
    Bei der Initialisierung eines sklaren Objektes, benötigen wir eine Wert für das Objekt, also ein Rvalue. Steht dort aber ein Lvalue, wird dieses implizit in ein Rvalue konvertiert (also der Wert gelesen).



  • camper schrieb:

    mit anderen Worten, der fehlerhafte Ausdruck ist kein modifizierbarer Lvalue. Da steht nicht, ob der Ausdruck zwar ein Lvalue sei, aber nicht modifizierbar (trifft in erste Zeile zu), oder schlicht kein Lvalue ist (die anderen 3 Fälle).

    Oh :), vielen Dank wiedermal an dich camper für die Aufklärung.



  • MrGentleman schrieb:

    Naja - schlechtes design - das ganze soll ein scanner für einen parser werden; und da muss ich halt abhängig des gelesenen zeichens ein entsprechendes token generieren.

    Das Interface von Base und Derived ergibt irgend wie überhaupt keinen Sinn. Wozu eine Ableitung und dort eine Implementation? Polymorphie kommt nicht zum Einsatz, und wenn sie zum Einsatz käme wäre wohl eine Factory Klasse als Singleton, die Objekte erzeugt, die dann die eigentliche Aufgabe übernehmen sinnvoller.

    MrGentleman schrieb:

    wie meinst du es mit delegates?

    Ein Implementierungsdetail ist damit gemeint. Meist taucht der Begriff in Zusammenhang mit SmallTalk Patterns auf z.B. mit Object Recursion - Chain of Command ist nur ein Spezialfall davon. Man kann in C++ Delegation via Funktionszeiger, Funktoren oder speziellen Klassen (Funktoren sind das ja auch) umsetzen, es kommt halt darauf an, was man braucht.



  • Nochmal nachgehakt:

    camper schrieb:

    Ein (skalares) Lvalue ist ein Ausdruck der auf ein (skalares) Objekt verweist, während ein Rvalue dieses Typs einen bestimmten Zustand oder Wert repräsentiert, den ein solches Objekt annehmen könnte.

    const int t  = 4;
    

    t ist somit ein Lvalue, aber ein nicht modifizierbarer ?


  • Mod

    KasF schrieb:

    Nochmal nachgehakt:

    camper schrieb:

    Ein (skalares) Lvalue ist ein Ausdruck der auf ein (skalares) Objekt verweist, während ein Rvalue dieses Typs einen bestimmten Zustand oder Wert repräsentiert, den ein solches Objekt annehmen könnte.

    const int t  = 4;
    

    t ist somit ein Lvalue, aber ein nicht modifizierbarer ?

    t ist eine konstante Variable.
    Der Ausdruck

    t
    

    ist ein nichtmodifizierbares Lvalue (Lvalue: Ausdruck verweist auf das skalare Objekt t; nichtmodifizierbar: da const-qualifiziert).



  • Simon2 schrieb:

    aber ganz überflüssig ist es nicht (und sei es nur, um aufgrund vom Input die richtige Klasse zu instantiieren).

    Es trifft aber nur auf die wenigen Fälle zu in denen man nur Integereingaben zu verarbeiten hat. Bei Scannern/Parsern hatte ich bisher immer das Glück, daß es sich um Zeichenketten handelte und nicht um einzelne Buchstaben.



  • MrGentleman schrieb:

    switch (ch) {
      case Scanner::_EOF:
        _token->type = Token::_EOF;
    	break;
    
      case MyConcreteScanner::QUOTE:
        _token->type = Token::QUOTE;
        nextChar();
    	break;
    

    Das meinte ich mit schlechtem Design (in Kombination zu Deinen erstem Posting). Du scheinst C und OOP Programmierung zu vermischen. Falls du wegen der Geschwindigkeit ein C-ähnlichen Ansatz brauchst, dann brauchst Du keine Vererbunghierachie, und wenn es OOP sein soll, was soll dann bitte "_token->type" für eine Aufgabe übernehmen?



  • Leute habe folgendes Programm geschrieben:

    #include <cstdlib>

    #include <iostream>

    using namespace std;

    int main(int argc, char *argv[])
    {

    int Preis, Rabatt, Endpreis ;

    cout << " Berechnung des Endpreises " ;

    cout << " Geben Sie den Preis ein " ;
    cin >> Preis ;

    cout << " 8% Großkunde, 5% Stammkunde oder 0% Sonstige ? " ;

    cin >> 8% >> 5% >> 0% ;

    int Auswahl ;

    switch(Auswahl)

    {
    case 1: Auswahl= 8% ;
    break ;

    case 2: Auswahl= 5% ;
    break ;

    case 3: Auswahl = 0% ;
    break ;

    default: Auswahl = 15% ;

    }

    Endpreis = Preis * 8% ;

    Endpreis = Preis * 5% ;

    Endpreis = Preis * 0% ;

    cout << " Endpreis ist gleich " << Endpreis ;

    system("PAUSE");
    return EXIT_SUCCESS;
    }



  • Ich hoffe, jemand kann mir helfen


Anmelden zum Antworten