Laufzeit vs. Compilezeit Polymorphismus
-
Ich arbeite schon lange mit C++, habe aber die templates immer etwas stiefmütterlich behandelt, wahrscheinlich, weil ich auch deren Nutzen nie so richtig kappiert habe.
Jetzt liest man aber immer mehr von Metaprogramming mittels templates, compiletime Polymorphismus und auch immer mehr Libraries verwenden solche Techniken und schliesslich scheint auch TR1 (und TR2) darauf näher einzugehen.
Kann mir einer erklären, wo ich in meiner objektorientierten Problemanalyse Ansätze erkennen kann, das Problem oder die Aufgabe mittels compiletime Polymorphismus zu lösen, anstatt klassich über Polymorphismus mit virtual functions. Wenn ich das richtig verstehe, gibt es nichts, was ich nicht über zuletzt genannten Weg auch lösen könnte, aber der Benefit des compiletime Polymorph. ist vorallem ein drastischer Speedgewinn. Wo setzt ihr andere da die Grenze zwischen positiver Effekt (Speed z.B.) und andererseits aufgeblasenem Code (Bloatware). Nicht zu vergessen sieht der Sourcecode für mich sehr sehr schwierig zu lesen/verstehen aus.
Ist das verständlich?
-
Ich mach mal ein Beispiel so wie ich es bis jetzt verstanden habe.
Laufzeit-Polymorphie:class Calculator { public: virtual int calculate(int i1, int i2) = 0; };
Von dieser Klasse kann man jetzt seine polymorphen Klassen ableiten (ja, ein triviales Beispiel...) zB:
class Adder : public Calculator { public: virtual int calculate(int i1, int i2) { return i1 + i2; } };
Dann kann man sowas machen:
Calculator* calc = new Adder; //... std::cout << calc->calculate(2, 3);
Mit statischer Polymorphie:
template <class Operator> class Calculator { public: int calculate(int i1, int i2) { return doOperation.operator()(i1, i2); } private: Operator doOperation; };
Die Operator-Klasse kann nun beliebig gewählt werden zB:
class Adder { public: int operator() (int i1, int i2) { return i1 + i2; } };
Das ganze wird dann so angewendet:
Calculator<Adder> calc; std::cout << calc.calculate(2, 3);
Vorteil: Das ganze wird bereits zur Kompilierzeit aufgelöst, während das Dynamische erst zur Laufzeit ausgewertet wird. Deshalb ist die statische Polymorphie nunmal nicht so dynamisch :xmas1:
Gruß
Don06
-
Danke für das gute Beispiel. Jetzt bin ich dem ganzen schon einen grossen Schritt näher gekommen. Wie entscheidet man jetzt aber, ober man dynamischen oder statischen Polymorphismus verwenden soll? Mir scheint es immer noch einen Mehraufwand an Tipparbeit und an intelektueller Leistung zu sein den statischen Weg zu gehen, so dass sich für mich das eher die Ausnahme darstellt. Konkret wenn es um Speed geht (eine Math-Library oder GUI-Library z.B.).
-
Wenn es laufzeit Polymorphie sein muss, dann geht es halt nur damit. Ich weiß auch nicht ob man die zwei irgendwie vergleichen sollte. Sind eigentlich doch unterschiedliche Sachen. Statische Polymorphie ist ein etwas "aufwendigerer Makroersatz" und laufzeit Polymorhpie ist mehr für "Programmsteuerung".
-
Laufzeit polymorphie nutzt du, wenn du sie brauchst, compilezeit polymorphie, wenn du etwas wiederverwendbar machen willst(also tipparbeit sparen).
-
Es stimmt, daß der Code mit Templates nicht ganz einfach zu lesen ist (selbst für mich nicht immer -).
Die Frage Compiletime- versus Laufzeitpolymorphie ist meistens eine grundlegende Designfrage. In rein objektorientierten Sprachen müssen alle Klassen von einer Basisklasse abgeleitet sein, in C++ dagegen nicht.
Wenn man jetzt aber Klassen nur mittels virtueller Funktionen erzeugt, so wird intern halt für jede virtuelle Methode ein Eintrag in der vtable erzeugt, der evtl. erst zur Laufzeit aufgelöst werden kann.Wenn aber die Klassen (bzw. Funktionen) eigentlich keinen (hierarchischen) Zusammenhang untereinander haben, sondern sich nur in den verwendeten Datentypen unterscheiden, so sollte man Templates einsetzen. Hierbei kann der Compiler dann den Code individuell optimieren (Nachteil ist halt, daß die Codegröße dabei ansteigt).
Du solltest als Einstieg in die Templates dir mal besonders die Algorithmen und Basis-Container der C++ Standard Bibliothek (auch STL genannt) anschauen.
Beim Erstellen von Templates geht man auch meist den Weg, daß man zuerst eine konkrete Klasse erstellt, und sobald diese fehlerfrei läuft, dann die verwendeten Datentypen und evtl. andere Parameter als Template-Parameter auslagert.
-
Danke für den Beitrag. Es beruhigt mich etwas, dass die Antwort von Euch nicht lautete: Was? Du verstehst das nicht? Ist doch soooo einfach... :xmas1:
Hatte zwischendurch mal eine Phase in der ich für einen Kunden in java programmieren musste. Eigentlich dachte ich dabei, dass ich mein Verständnis für OO dort vertiefen konnte, da ich früher häufig ehrlich gesagt häufig einfach C mit einigen C++-Features programmiert habe. Dann kehrte ich vor einem Jahr wieder zu C++ zurück und irgendwie ist "alles" anders geworden. Nur noch templates hier und da...
Also, danke nochmals für die Antworten.
-
Wenn du bereits zur Compilezeit weißt, welche Klassen du verwenden willst, solltest du statische Polymorphie verwenden. Der Compiler kann das dann einfacher auflösen und besser optimieren, und du sparst dir die extra Dereferenzierung über die vtable bei dynamischer Polymorphie. Wenn das Verhalten der Typen (also ihre 'wirkliche Klasse') erst zur Laufzeit feststeht, kommst du um dynamische Polymorphie nicht herum. Bei obigem Calculator-Beispiel z.B. wäre die Entscheidung abhängig davon, wo du diesen Calculator einsetzen willst. Wenn du von vornherein weißt, wo du welche Operation ausführst (z.B. ein simples Buchhalterprogramm), dann bietet sich statischer Polymorphismus an. wenn die Art der Operation aber z.B. von einer Nutzereingabe abhängig ist, kommst du um dynamische Polymorphie nicht herum.