Rein virtuelle Methoden / Abstrakte Klasse



  • Habe eine spezielle Frage zu rein virtuellen Methoden abstrakter Klassen:

    Ist ein Down-Cast von abstrakten Klassen eigentlich noch von Nöten?

    kleines Beispiel:

    class Abstrakt                          
    {
        protected:
            Abstrakt(){}                    
        public:
            virtual void ausgabe() = 0;
    };
    
    class Nichtabstrakt : public Abstrakt
    {
        public:
            void ausgabe()
            {
                cout << "Ich bin nicht"
                     << "mehr abstrakt"
                     << endl;
            }
    };
    
    main()
    {
        Abstrakt* feld[5];                    
    
        feld[0] = new Nichtabstrakt;
        feld[0]->ausgabe();          // = ((Nichtabstrakt*)feld[0])->ausgabe();
                                     // Zufall ??
    
        getchar();
        return 0;
    }
    

    Ist das nur in meinem Beispiel so, dass beim Aufruf einer Down-geCasteten (abstrakten Klassen)funktion die Funktion diegleiche ist wie ohne Cast?



  • oh, an dem quelltext würden wohl einige leute ziemlich viel auszusetzen haben.

    Ist ein Down-Cast von abstrakten Klassen eigentlich noch von Nöten?

    wenn du ständig downcasten musst, stimmt etwas mit deinem design nicht. virtuelle methoden in basisklassen sind doch genau dafür da, sich das gecaste zu ersparen, weil automatisch die richtige methode aufgerufen wird.
    btw. kannst du in deinem beispiel sowohl auf den konstruktor verzichten als auch darauf, ihn protected zu machen, weil du abstrakte klassen sowieso nicht instanziieren kannst.
    abgesehen davon verwendest du den gefährlichen (falschen, nie zu verwendenden) C-cast. zum downcasten immer dynamic_cast verwenden (außer du weißt, welchen typ du tatsächlich hast, dann kannst du static_cast verwenden).
    und abgesehen davon fehlt dir ein delete 😉



  • queer_boy schrieb:

    oh, an dem quelltext würden wohl einige leute ziemlich viel auszusetzen haben.

    Da hast du vollkommen recht. Alleine schon weil sich der Code auf einen Standardkonformen Compiler nichtmal compilieren liese...

    Quellcode schrieb:

    Ist ein Down-Cast von abstrakten Klassen eigentlich noch von Nöten?

    Nur falls du auf etwas zugreifst, das nicht über die entsprechende Basisklasse erreichbar ist. Siehe Kommentar von queer_boy

    So nun die wesentlichen anderen Fehler:
    a) In eine Basisklasse gehört ein virtueller Destruktor (Der Ausnahmefall ist zu selten, als das ich den hier erwähne).
    b) Wenn eine rein virtuelle Methode vorhanden ist, brauchst du mit sicherheit keinen Konstruktor zu definieren, da die Klasse ohnehin nicht instanzierbar ist (Kleiner Trick: Falls man die Basisklasse nicht instanzierbar haben will, aber keinen Kandidat für eine rein virtuelle Methode findet, kann man den Destruktor rein virtuell deklarieren, muss ihn aber dennoch ausdefinieren).
    c) Kleiner Vorgriff da man an deinen Code die Befürchtung haben kann das du es machst: Niemals using namespace im Header einsetzten.
    d) "main()" gibt es nicht. Wenn dein Compiler das ohne Fehler oder zumindestens einer Warnung durchlässt, würde ich mir dringend einen anderen suchen. Es heist "int main()".
    e) Ich bin kein Freund von Arrays, aber das ist hier unwichtig. Wichtiger ist eigentlich das du ein Array sinnvoll initialisieren würdest, und sei es das jeder eintrag mit 0 initialisiert wird.
    f) Wenn du speicher dynamisch allozierst, musst du ihn auch wieder am Schluß freigeben (Zu jedem new gehört ein delete, zu jedem new[] ein delete[]).

    cu André



  • Danke für die vielen Hinweise auf meine Fehler in dem Beispiel.

    1. ständiges downcasten? die frage war warum die funktion die Gleiche ist
    2. wollte ich nur wissen ob man ein Down-Cast bei abstraktne klassen überhaupt
    braucht
    3. weiß ich, dass ich den Konstruktor nicht brauche, wollte lediglich festlegen,
    dass es sich wirklich um eine abstrakte Klasse handelt.
    4. weiß ich, wie man sicher downcastet, es ging nur darum zu wissen, warum die
    Ausgaben diesselben sind. Sucht sich der Compiler automatisch die passende
    Methode?

    5. mit dem fehlenden delete hast du vollkommen recht^^



  • Quellcode schrieb:

    die frage war warum die funktion die Gleiche ist [...] Sucht sich der Compiler automatisch die passende Methode?

    Man könnte sagen: Herzlichen Glückwunsch, Du hast den Sinn von virtual demonstriert 😃


  • Administrator

    LordJaxom schrieb:

    Quellcode schrieb:

    die frage war warum die funktion die Gleiche ist [...] Sucht sich der Compiler automatisch die passende Methode?

    Man könnte sagen: Herzlichen Glückwunsch, Du hast den Sinn von virtual demonstriert 😃

    Wenn ich dich etwas korrigieren darf?

    @Quellcode,
    Nein der Compiler sucht nicht die richtige Methode. Dies geschieht zur Laufzweit. Da wird automatisch ermittelt, welche Funktion diejenige überschreibt und dann entsprechend aufgerufen. Das ist ja eben auch der Sinn von virtual Methoden.

    Und betonen möchte ich virtual Methoden, da virtual auch im Zusammenhang mit Basisklassen auftauchen kann. Aber das hat wiederum mit dem Ableiten von mehreren Klassen zu tun, was besser erst später erwähnt wird 😃

    Im übrigen empfehle ich ein C++ Tutorial oder Buch, wenn dir diese Tatsache mit den virtual Methoden noch nicht bewusst war 😉

    Grüssli



  • Dravere schrieb:

    ...Nein der Compiler sucht nicht die richtige Methode. Dies geschieht zur Laufzweit. Da wird automatisch ermittelt, ...

    Ich glaube nicht, dass "Quellcode" diese Unterscheidung im Sinn hatte, als er das schrieb ... sondern eher "automatisch" meinte.

    Aber gut, dass Du es nun präzisiert hast.

    Gruß,

    Simon2.


  • Administrator

    Simon2 schrieb:

    Ich glaube nicht, dass "Quellcode" diese Unterscheidung im Sinn hatte, als er das schrieb ... sondern eher "automatisch" meinte.

    Das Problem ist, man kann dies nicht wissen. Erst recht nicht, wenn er nicht weiss, was virtuelle Methoden sind. Lieber die Sache richtig hinschreiben, als falsch stehen zu lassen. Das verhindert Missverständnisse.

    Simon2 schrieb:

    Aber gut, dass Du es nun präzisiert hast.

    Kann es sein, dass dieser Satz ironisch gemeint war? Denn präzisiert ist ja irgendwie übertrieben. Ich habe nur das Compiler durch Laufzeit ersetzt. Mit virtuellen Tabellen und Zeigern wollte ich jetzt nicht kommen, das wäre wohl zu viel gewesen 😉

    Grüssli



  • Ja, danke für die Hilfen.
    bin grade erst dabei, das Ganze ein bisschen zu verstehen.
    Ich habe bereits "gewusst", dass der die Methode zur Laufzeit ausgewählt wird,
    nur nicht so ganz verstanden und es mir quasi damit selbst erklärt, jetzt weiß ich mehr^^



  • Dravere schrieb:

    ...

    Simon2 schrieb:

    Aber gut, dass Du es nun präzisiert hast.

    Kann es sein, dass dieser Satz ironisch gemeint war? ...

    😮
    Nein, das war nicht ironisch gemeint - so wie das hier auch nicht.

    Ich hatte das schon richtig verstanden und meinte den Dank für die "Präzisierung" ernst.

    Gruß,

    Simon2.

    P.S.: Da es letztlich (und im Internet erst Recht) unmöglich ist, "Nicht-Ironie" zu beweisen, wirst du mir letztlich glauben müssen....



  • Ich habe btw. auch ein Buch hier, in dem steht, dass der Compiler das machen würde .. dieser Fehler scheint also etwas verbreiteter zu sein. In einem Anderen wird auf die vtables verwiesen und klar gemacht, dass solche Vererbungen ineffizient sind und man es deshalb bei Anwendungen, bei denen es um jeden Frame geht, nicht mit der Vererbung übertreiben soll, was dann wohl eher richtig ist.



  • sorry, aber vererben und ineffizient sein passt so nicht einfach zusammen.
    entweder du hast nicht richtig paraphrasiert oder das buch ist ebenfalls nicht eines der besten.



  • ich kann mir nicht vorstellen, das vererben ineffizient sei...
    Wenn ich mir da die Klassenhierarchie der VCl anschaue...
    da gibts etliche Ableitungen



  • die ableitungen sind auch kein problem. (wenn man es mit der tiefe nicht zu sehr übertreibt und so den ctor dank aufruf aller eltern-ctors drastisch aufbläht. aber selbst das spielt bei einem halbwegs sauberen design keine überwältigende rolle, da objekte deutlich länger zur verarbeitung denn zur erzeugung brauchen sollten.)
    die virtual-funktionen sind langsamer im aufruf als nicht virtual, da immer erst in der vtable geschaut werden muss, wo die funktion ist, was aber kaum eine rolle spielt, da auch hier gilt: wenn die funktion nicht trivial ist, liegt die ausführzeit deutlich über der aufrufzeit.

    was schon eher ein problem sein kann, ist, dass die virtual-funktionen auf manchen prozessoren das cache-handling und die branch-prediction zerpflücken können (wobei die prozessoren in dem bereich deutlich besser geworden sind als früher). das ist tatsächlich lässtig. aber hier gilt: erst schauen, ob das wirklich passiert und dann überlegen, wie, oder in vielen fällen wohl eher, ob man das umgehen kann.


  • Administrator

    Simon2 schrieb:

    😮
    Nein, das war nicht ironisch gemeint - so wie das hier auch nicht.

    ...

    P.S.: Da es letztlich (und im Internet erst Recht) unmöglich ist, "Nicht-Ironie" zu beweisen, wirst du mir letztlich glauben müssen....

    Ich bin dann wohl ein wenig paranoid 🙂
    Naja. Ich fand es halt einfach irgendwie witzig, weil so viel präziser war es ja eben nicht. Aber egal.

    Grüssli


Anmelden zum Antworten