Anfänger: Vererbung / Casting



  • Hi Leute!

    Ich hab da anscheinend irgendwas noch nicht so richtig verstanden. Also hier mal meine Klassen, und weiter unten die Fragen:

    #define DB_OK                 0
    #define DB_ERROR             -1
    #define DB_ERROR_ISCONNECTED -2
    
    class DB_return
    {
        private:    
        int type; // entweder DB_OK oder DB_ERROR
    
        public:    
        DB_return(int);  // setzt this->type auf DB_OK oder DB_ERROR
        bool is_error(); // sollte wohl klar sein
    };
    
    class DB_error : public DB_return
    {
        private:
        int    error_id; // die Fehlernummer
        char * error_msg // eine Fehlernachricht
    
        public:
        DB_error(int id); // ruft DB_return(DB_ERROR) auf und
                          // setzt this->error_id = id
    
        int    get_error_id(); // sollte klar sein
        char * get_message();  // sollte klar sein
    };
    
    class DB_result : public DB_return
    {
        // diese Klasse bekommt noch mehr Inhalt :-)
        public:
        DB_result() : DB_return(DB_OK) {};
    };
    

    Wenn ich jetzt also folgendes Versuche geht das schon gut, aber...
    [cpp]
    db_return = new DB_error(DB_ERROR_ISCONNECTED);
    if (db_return->is_error()) {
    std::cout << "Fehler aufgetreten: " << *((DB_error ) db_return)->get_message() << '\n';
    } else {
    std::cout << "kein Fehler" << '\n';
    }
    [/cpp]
    (1) der Cast stört mich! Kann ich den nicht irgendwie weglassen, bzw. meine Klassen so umbauen, dass das möglich ist?

    (2) In der Basisklasse möchte ich die Methode get_messsage() nicht virtuell deklarieren, da sie in der Unterklasse DB_result nix zu suchen hätte! Ist doch richtig?

    (3) Ausserdem sollte die Klasse DB_return sowieso eher Abstrakt sein, so dass man davon keine Instanzen bekommen kann. Stimmt das?

    (4) Ich könnte is_error() virtuell deklarieren und dann in den Unterklassen direkt mit return true, bzw. return false implementieren. Damit spare ich mir einen Vergleich (if (this->type == DB_ERROR) ...), macht das Sinn?

    Grüße, L.



  • Zu 4.)
    - Ist in jedem Fall besser.

    Zu 3.)
    - Abstract macht so wie es jetzt aussieht Sinn. Da in der Klasse DB_error dein "type"-member garnicht nutzt wird, würde sogar eine pure virtuell Implementierung deiner Basisklasse Sinn machen.
    struct DB_return
    {
    bool is_error() = 0;
    }
    Wenn man dann jedoch, sieht das eigentlich nur eine Funktion übrigbleibt, Frage ich mich ob DB_return noch einen grossen Nutzen hat.

    Wenn ich mal annehme das es bei dem Ganzen um die Ergbnisse von Datenbankoperationen geht, würde ich mir den Return für den Erfolgsfall ganz sparen und für den Fehlerfall ne Exception werfen. Am besten definierst du ne eigene Exceptionklasse, die du dann beim catch() rausfiltern kannst.
    In diesem Sinne würden sich 1. und 2. von selbst erledigen.

    mfg JJ



  • Ok, soweit alles angekommen! Danke.
    Die Klasse DB_result wird natürlich noch erweitert und soll später das Ergebnis eines SQL-Queries halten und ausliefern können. Ich dachte mir eben es währe nett wenn bei einem Fehler statt einem DB_result, ein DB_error Objekt (auch dieses wird noch erweitert) zurück kommt, welches nähere Informationen zu dem aufgetretenen Fehler enthält. Wie mann mit dem Fehler dann umgeht ist ganz belibig.

    Zu der Cast-Sache hätte ich gerne noch was gewusst! Kann ich den nicht irgendwie vermeiden? Mir ist schon klar, dass der Pointer vom Typ DB_return ist und damit nichts von der Methode get_message() weiß, aber gibts da nicht ne Möglichkeit?

    Grüße, L.



  • Wie du schon selbst sagst DB_result wird erweitert. Dein Problem liegt aber meines Erachtens in der Fehlerbehandlung. Du solltest hier C++-like mit Exceptions arbeiten.
    Wenn also eine Funktion XXXSelect() aufgerufen wird, die dir etwas in der Art eines Recordset zurückliefert, dann kann dieses Recordset-Objekt natürlich verschiedene Informationen enthalten. Aber nur wenns geklappt hat. Im Fehlerfall bitte Exception werfen. Wenn du es so machst brauchts du die Unterscheidung wie du sie bisher hast nicht mehr. Gerade bei Datenbanken gibt es eigntlich keine "Grauzonen", entweder es hat geklappt dann is gut und weiter, oder es hat nicht geklappt und Abbruch. Abbruch muss nicht heissen Programmende, aber ein sinnvolles weiterarbeiten an der Aufgabe die datenbankmässig gerade gescheitert ist, ist kaum möglich.

    Wenn du nicht beiden Klassen ein get_Message() spendierst fällt mir auch nicht (einfaches) besseres ein 😞

    mfg JJ


Anmelden zum Antworten