Was hat virtual für einen Sinn?



  • C_Cheaf schrieb:

    Ok jetzt wird die Methode von Base ausgeführt. Aber warum sollte ich als Pointer Base* verwenden und nicht Derived* ?

    Mach doch mal in Base die Methode "foo" virtual, und schau was passiert...
    Stichwort Polymorphie, Basisklassenzeiger.



  • C_Cheaf schrieb:

    Ok jetzt wird die Methode von Base ausgeführt. Aber warum sollte ich als Pointer Base* verwenden und nicht Derived* ?

    Abstraktion. Du willst dir schliesslich die Möglichkeit erhalten, später auf AnotherDerived zu zeigen, ohne die Schnittstelle ändern zu müssen.



  • Est ist einfach viel leichter und nicht so aufwendog.
    Hast du später zim beispiel noch mehrere von base abgeleitete klassen die alle eigentlich ne gleiche funktion haben.
    Bzw halt mit einer anderen implementierung, kannst grad ein zeiger auf base an eine funktion ubergeben und diese ruft dann automatisch die richtige funktion auf.
    Ist ja leichter als in der funktion dann jede möglichkeit reinzuschreiben.

    Hoffe du verstehst was ich mein;-)



  • Ok ich glaube jetzt habe ich es verstand.

    Ist es eigentlich sinnvoll immer, sobald es möglich ist, virtu einzusetzen ein zusetzten, oder nur dann, wenn es wirklich nötig ist? Denn virtual macht die Sache wegen der Tabelle mit den virtuellen Methoden doch langsamer, oder nicht?

    Wenn man nun zum Beispiel DirectX Programme schreiben will, ist es doch äußerst wichtig nicht unnötig Ressourcen zu verblasen. Eignet sich für zeitkritische Anwendungen objektorientierte Programmierung überhaupt?



  • C_Cheaf schrieb:

    Ist es eigentlich sinnvoll immer, sobald es möglich ist, virtu einzusetzen ein zusetzten, oder nur dann, wenn es wirklich nötig ist? Denn virtual macht die Sache wegen der Tabelle mit den virtuellen Methoden doch langsamer, oder nicht?

    Genau. Deshalb sollte man virtual auch nur einsetzen, wo es wirklich notwendig ist. Ganz wichtig ist zu wissen, dass eine Vererbungshierarchie nicht zwangsläufig dynamische Polymorphie erfordert. Deshalb sollte man Basisklassen nicht apriori mit virtuellen Funktionen (z.B. Destruktor) versehen.

    C_Cheaf schrieb:

    Wenn man nun zum Beispiel DirectX Programme schreiben will, ist es doch äußerst wichtig nicht unnötig Ressourcen zu verblasen. Eignet sich für zeitkritische Anwendungen objektorientierte Programmierung überhaupt?

    Ja, definitiv. Erstens fallen virtuelle Funktionsaufrufe kaum ins Gewicht, wenn du rechenintensive Vorgänge wie Rendering und die damit verbundene Logik hast. Zweitens musst du dran denken, dass virtual nur eine von vielen Möglichkeiten ist, je nach Situation anderes Programmverhalten zu haben. In C könnte man eine solche Fallunterscheidung über if , switch oder Funktionszeiger lösen, welche aber ebenfalls Performance brauchen.

    Abgesehen davon ist Laufzeitpolymorphie nur ein kleiner Teil der objektorientierten Programmierung. OOP als Konzept kann man auch in C realisieren, auch wenn gewisse Möglichkeiten schwieriger oder gar nicht umzusetzen sind. In diesem Zusammenhang ist es auch sinnfrei, OOP generell als "langsam" zu bezeichnen, da man sich damit lediglich auf konkrete Implementierungen des Konzepts bezieht.



  • Der Overhead durch den VTable-Lookup ist minimal. Sobald die Funktion wirklich was tut, merkst du rein gar nix mehr.
    Bei kleinen Funktionen, die der Compiler ohne virtual Inlinen könnte (tut er auch mit virtual immer noch, wenn es möglich ist) kann der Overhead schon spürbarer werden - aber wirklich nur dann wenn du nur solche Funktionsaufrufe hast.

    virtual aber prinzipiell nur dann, wenn nötig. Wenn du erwarten kannst, dass du Runtime-Polymorphie benötigst! Wenn das so ist, bastel dir bitte keine eigene Lösung mit irgend welchen enums und static_casts und sonstwas, weil das am Ende sicherlich langsamer und vor allem deutlich unhandlicher und unübersichtlicher ist, als virtual zu verwenden.

    Eine gute Alternative wäre Compiletime-Polymorphie -> templates! Ist aber auch nicht immer möglich und sinnvoll.



  • Ok danke für die ausführlichen Antworten!

    Ich habe im Tutorial weitergelesen und bin nun bei virtual und abstrakten Klassen. Eigentlich ganz simpel und einfach DOCH:

    Die Deklaration virtual void header() = 0; bedeutet, dass header eine rein virtuelle (pure virtual) Methode ist. Rein virtuell heißt, dass sie von jeder abgeleiteten Klasse überschrieben werden muss. Es bedeutet aber nicht, dass sie keinen Körper haben kann:

    #include<iostream>
    
        class Base
        {
        public:
          virtual ~Base() {}
          virtual void foo() = 0;
          // = 0 und Methodenkörper können nicht
          //gleichzeitig auftreten, wir müssen foo
          //deshalb gesondert implementieren
        };
    
        class Derived : public Base
        {
        public:
          void foo()
          {
            cout<<"Derived::foo\n";
            Base::foo();
          }
        };
    
        void Base::foo()
        {
          cout<<"Base::foo\n";
        }
    
        int main()
        {
          Derived d;
          d.foo();
        }
    

    Was hat denn das für einen Sinn bitte??? Warum bekommt virtual void foo() nun einen Funktionskörper, auch wenn man von Base gar keine Instanz erstellen kann? Dann kann man diesen Funktionskörper doch nie ausführen?!



  • Doch, durch das Base::foo(); kann diese Funktion angesprochen werden.



  • Weil das virtual [...] =0 bedeutet, dass die Methode pure virtual ist und von abgeleiteten Klassen implementiert werden MUSS!
    Eine Implementierung kann aber durchaus sinn machen, um Codeduplizierung zu sparen. Mir fällt grad kein Beispiel ein, wo das nützlich sein kann, darum einfach ein Std-Testbeispiel:

    #include <iostream>
    using namespace std;
    
    struct Base
    {
        virtual ~Base() {}
        virtual void foo() const=0;
    };
    
    void
    Base::foo() const
    {
        cout << "Base::foo" << endl;
    }
    
    struct Derived : public Base
    {
        void foo() const {
            cout << "Derived::foo" << endl;
            Base::foo();  // rufe Basisimplementierung auf.
        }
    };
    
    int main()
    {
        Base* b = new Derived;
        b->foo();
        delete b;
        //b = new Base;  // <-- das geht nicht!
    }
    


  • Verstehe.

    Danke für eure schnellen Antworten!



  • Beißt sich da nicht die Pure Virtual Methode aus Base mit der Implementierung

    struct Base
    {
        virtual ~Base() {}
        virtual void foo() const=0; //Pure Virtual
    };
    
    void
    Base::foo() const   //??? Nun Pure Virtual oder doch implementiert
    {
        cout << "Base::foo" << endl;
    }
    


  • Das ist erlaubt. Üblicherweise sieht man sowas aber eher beim Destruktor, wenn überhaupt (sonst könnte man gar keine rein virtuellen Destruktoren einrichten).



  • das es erlaubt ist weiß ich auch aber für den Leser des Codes beißt sich das für mich. Wenn ich eine Pure Virtuel Methode seh denk erwarte ich keine Implementierung.
    Mein Post zielte nur auf einen "sauberen" Stil ab


Anmelden zum Antworten