Statische versus dynamische Polymorpie



  • Hallo zusammen,

    ich bin mir über den Unterschied im Klaren.
    Statische Polymorpie: Auflösung zur Kompilierzeit
    Dynamische Polymorpie: Auflösung zur Laufzeit

    Welche Kriterien berücksichtigt ihr für die Auswahl einer dieser beiden
    Typen? Generell kann man doch beide (nahezu) äquivalent umwandeln, oder?
    Ich sehe häufig, dass in C++ die statische Polymorphie vorgezogen wird.
    Liegt das daran, dass die Standard-Library (wegen Performancevorteilen(?))
    auf statischer Polymorphie basiert? Der Copy-Konstruktor ist vielleicht auch noch
    ein Vorteil von statischer Polymorpie (weniger Aufwand gegenüber clone-Funktion).
    Statische Polymorphie benötigt (generell) etwas weniger Code ^^
    Welche Vorteile bietet denn dynamische Polymorphie, wenn man sie nicht für
    externe Bibliotheken braucht oder dynamisches Laden für Plug-Ins?

    Gruß,
    Polymorph



  • dynamisch muß gemacht werden, wenn von den benutzereingaben abhängig ist, was genau gemacht wird.



  • Stimmt, wenn ich es mir genau überleg.
    Da hast du völlig recht.

    Danke für das Kriterium!



  • Polymorph schrieb:

    ...
    Ich sehe häufig, dass in C++ die statische Polymorphie vorgezogen wird.
    Liegt das daran, dass die Standard-Library (wegen Performancevorteilen(?))
    auf statischer Polymorphie basiert? ...

    Ich glaube eher, dass Beides denselben Grund hat: Nach C++-Philosophie ist es besser, wenn Fehler zur Compilezeit (= Entwicklungsphase -> Entwickler findet den Fehler) auftaucht als zur Laufzeit (=Tester oder Kunde findet den Fehler).

    Polymorph schrieb:

    ...
    Der Copy-Konstruktor ist vielleicht auch noch
    ein Vorteil von statischer Polymorpie (weniger Aufwand gegenüber clone-Funktion)....

    Da sehe ich nicht, wo die beiden sich (bzgl. Aufwand) unterscheiden sollten. Wenn ein neues Objekt auf Basis eines bestehenden angelegt werden soll, muss man dasselbe tun ...
    Auch sehe ich da eher einen Unterschied in statischer bzw. dynamischer Speicherverwaltung (ich weiß nicht, wie ein clone() mit statischer SP-Verw. funktionieren kann) - was aber nichts mit statischer bzw. dynamischer [u]Polymorphie[/i] zu tun hat.

    Polymorph schrieb:

    ...Statische Polymorphie benötigt (generell) etwas weniger Code ^^...

    Glaube ich auch nicht. 😉

    Gruß,

    Simon2.



  • Simon2 schrieb:

    Da sehe ich nicht, wo die beiden sich (bzgl. Aufwand) unterscheiden sollten. Wenn ein neues Objekt auf Basis eines bestehenden angelegt werden soll, muss man dasselbe tun ...

    Ein clone ist haesslich. Es wirft viele Fragen auf (wer reserviert speicher, wer gibt ihn frei,...) und braucht daher einen größeren Aufwand.

    Polymorph schrieb:

    ...Statische Polymorphie benötigt (generell) etwas weniger Code ^^...

    Glaube ich auch nicht. 😉

    Allein dass bei statischer polymorphie alle interfaces implizit sind, spart schon deutlich code.



  • Shade Of Mine schrieb:

    ...Ein clone ist haesslich. ...

    Finde ich auch.
    Darum ging es mir aber auch nicht.

    Shade Of Mine schrieb:

    ...Allein dass bei statischer polymorphie alle interfaces implizit sind, spart schon deutlich code.

    Hast Du da ein Beispiel für?

    Meinst Du sowas:

    struct dynIF {
       virtual void f() {}
    };
    
    struct dynIF1 : dynIF {
       virtual void f() {}
    };
    
    // vs.
    template <typename T>
    struct statIF {
       void f() {  }
    }
    

    ?
    Da sähe ich erstmal keine Ersparnis, weil die Code-Unterschiede letztlich irgendwo implementiert werden müssen (dann eben in den unterschiedlichen Ts)...

    Gruß,

    Simon2.



  • volkard schrieb:

    dynamisch muß gemacht werden, wenn von den benutzereingaben abhängig ist, was genau gemacht wird.

    Was doch eigentlich der häufigste Fall ist, oder? Denn die meisten Anwendungen verarbeiten Daten vom Anwender. Statische Polymorphie habe ich bisher eigentlich nie eingesetzt, weil ich bisher keinen Fall hatte. Ich habe das Buch "Modernes C++ Design" und 90% des Buches ist für mich nutzlos. Weil dort viel von "Daten werden vom Programmierer vorgegeben" ausgeht.

    Oder übersehe ich da etwas? Vielleicht kann mir jemand ein Bsp. nennen, wo statische Polym. täglich vorkommt?



  • Artchi schrieb:

    Oder übersehe ich da etwas? Vielleicht kann mir jemand ein Bsp. nennen, wo statische Polym. täglich vorkommt?

    std::vector schon einmal verwendet?
    Das ist statische Polymorphie. functors schonmal verwendet? Das ist statische Polymorphie.

    Jedes deiner C++ verwendet statische Polymorphie.

    @Simon2:
    dynamische polymorphie verlangt interfaces oder abcs. Das gibt es bei statischer nicht. und ein interface korrekt zu designen ist nicht trivial.



  • Artchi schrieb:

    Ich habe das Buch "Modernes C++ Design" und 90% des Buches ist für mich nutzlos. Weil dort viel von "Daten werden vom Programmierer vorgegeben" ausgeht.

    Eines der besten C++-Bücher. Mein Beileid. Aber dann solltest Du dich von C++ trennen, dann gibt es kaum eine schlechtere Sprache für deine Sicht der Dinge.



  • Shade Of Mine schrieb:

    ...@Simon2:
    dynamische polymorphie verlangt interfaces oder abcs. ...

    ("abc" ?)
    statische Polymorphie verlangt doch ebenfalls Interfaces ... nur (leider ?) sehr implizit formuliert und mit deutlich mehr Interpretationsspielraum (durch reine "Namensauflösung")....

    Gruß,

    Simon2.



  • Shade Of Mine schrieb:

    Artchi schrieb:

    Oder übersehe ich da etwas? Vielleicht kann mir jemand ein Bsp. nennen, wo statische Polym. täglich vorkommt?

    std::vector schon einmal verwendet?
    Das ist statische Polymorphie. functors schonmal verwendet? Das ist statische Polymorphie.

    Ja, diese ganzen Dinge benutze ich regelmäßig und gerne. Aber dann verstehe ich wohl unter statischer Polymorphie nicht das, was ich schon benutze... 🙄



  • Simon2 schrieb:

    ("abc" ?)
    statische Polymorphie verlangt doch ebenfalls Interfaces ... nur (leider ?) sehr implizit formuliert und mit deutlich mehr Interpretationsspielraum (durch reine "Namensauflösung")....

    abc == abstract base class

    Der unterschied ist, bei dynamischer polymorphie musst du vorher die interface anforderungen kennen. bei statischer ist sie variable. solange du feature X vom Typ T nicht verwendest, braucht T das feature nicht zu haben. das spart logischerweise arbeit.

    @Artchi:
    Was verstehst du denn unter polymorphie?



  • Shade Of Mine schrieb:

    Der unterschied ist, bei dynamischer polymorphie musst du vorher die interface anforderungen kennen. bei statischer ist sie variable. solange du feature X vom Typ T nicht verwendest, braucht T das feature nicht zu haben. das spart logischerweise arbeit.

    Da sollte man aber dazu sagen, dass das nichts mit statischer und dynamischer Polymorphie an sich zu tun hat, sondern mit der Art und Weise, wie C++ diese umsetzt. Es spricht prinzipiell nichts dagegen, auch dynamisch "Duck-Typing" anzuwenden (siehe Smalltalk oder diverse Scriptsprachen), genauso wie man auch statisch ein bestimmtes Interface vorgeben kann.



  • Shade Of Mine schrieb:

    Simon2 schrieb:

    ("abc" ?)
    statische Polymorphie verlangt doch ebenfalls Interfaces ... nur (leider ?) sehr implizit formuliert und mit deutlich mehr Interpretationsspielraum (durch reine "Namensauflösung")....

    abc == abstract base class...

    Danke! 🙂

    Shade Of Mine schrieb:

    ...
    Der unterschied ist, bei dynamischer polymorphie musst du vorher die interface anforderungen kennen. bei statischer ist sie variable....

    "Bekannt" und "variabel" für mich nicht unbedingt Gegensätze.
    Sowohl dynamisch wie auch statisch muss der "umsetzende Typ" bestimmte Bedingungen erfüllen - und diese Bedingungen kann man im Nachinein "aufweiten" oder "einengen" (durch Hinzufügen/Weglassen von virtuellen Funktionen bzw. "Features").

    Bekannt müssen sie in beiden Fällen sein - nur im Fall von "Polymorphie via virtual functions" sind sie besser dokumentiert aber auch eingeschränkter (letztlich nur "Funktionssammlung").

    Gruß,

    Simon2.



  • Shade Of Mine schrieb:

    @Artchi:
    Was verstehst du denn unter polymorphie?

    Mhrere bzw. vielfältige Typen von Objekten einer Basis. Wobei z.B. durch Late Binding eine Behandlung stattfinden kann, in dem zur Laufzeit der Typ ermittelt wird. Gut, das ist für mich dynamische Polymorphie.

    Wenn mich jemand fragt, was ich unter statische Polymorphie verstehe. Brauche ich in meinem vorherigen Absatz Late durch Early ersetzen, und Laufzeit durch Compilezeit.

    OK, vector<T> wird zur Compilezeit mit einem bestimmten Typen gebaut. Hem... ja... ist mir ehrlich gesagt zu trivial für Polymorphie! 😃 Im ernst, wenn DAS die ganze Story war... dann weiß ich nicht, warum ich "Modernes C++ Design" (MCD) lesen muß?

    In MCD habe ich ganz andere Sachen gelesen, nämlich das ich über Template-Metacode entscheiden kann, welcher Typ aus einer Vererbungshirarchie zur Compilerzeit gewählt werden soll.

    Das ist aber von vector, functor etc. meilenweit entfernt, und würde diese nicht als Polymorphie bezeichnen. Denn wenn ich std::vector<std::string> definiere, ist das für mich eine eindeutige Typdefinition, wo ich keinen Typ zur Compilezeit wähle! Ich wähle sie schon im Code. Der Compiler muß keine Entscheidung mehr vornehmen. Selbst vector und string sind nicht mal in einer Typhirarchie, so das eine Vielfältigkeit (Polymorphie) vorhanden wäre. Es ist einfach nur eine sture Typersetzung...

    So habe ich das bisher immer gesehen.



  • Statische Polymorphie ist, wenn bei x+y an der einen Stelle was total anderes ausgeführt wird als bei x+y an der anderen Stelle, weils einmal ints und beim anderen mal double sind.



  • Artchi schrieb:

    Der Compiler muß keine Entscheidung mehr vornehmen. Selbst vector und string sind nicht mal in einer Typhirarchie, so das eine Vielfältigkeit (Polymorphie) vorhanden wäre. Es ist einfach nur eine sture Typersetzung...

    Du definierst Polymorphie über eine Entscheidung des Compilers? Dann wäre zB Ducktyping garkeine Polymorphie?

    Ich sehe polymorphie eher so:
    ich sage:
    a.foo()
    und es macht das eine, dann sage ich mit einem anderen a nochmals a.foo() und es macht was anderes.

    + und auch << sind hier sehr schöne operatoren die polymorphes verhalten zeigen.

    bei einem container ist es ja zB so, dass dem container komplett egal ist was T für ein Typ ist. Für dich als anwender des containers ists natürlich nicht wirklich spannend. weil du sagst "gib mir container von T" und den bekommst du einfach. functors sind da schon etwas spannender. du sagst apply() dies, und apply() das und es passieren 2 unterschiedliche dinge.



  • Shade Of Mine schrieb:

    Artchi schrieb:

    Der Compiler muß keine Entscheidung mehr vornehmen. Selbst vector und string sind nicht mal in einer Typhirarchie, so das eine Vielfältigkeit (Polymorphie) vorhanden wäre. Es ist einfach nur eine sture Typersetzung...

    Du definierst Polymorphie über eine Entscheidung des Compilers?

    Ja, bei statischer Polymorphie.

    Shade Of Mine schrieb:

    Ich sehe polymorphie eher so:
    ich sage:
    a.foo()
    und es macht das eine, dann sage ich mit einem anderen a nochmals a.foo() und es macht was anderes.

    Ja, zur Laufzeit aber. Ist was anderes als statisch zur Compilezeit.

    Shade Of Mine schrieb:

    bei einem container ist es ja zB so, dass dem container komplett egal ist was T für ein Typ ist. Für dich als anwender des containers ists natürlich nicht wirklich spannend. weil du sagst "gib mir container von T" und den bekommst du einfach. functors sind da schon etwas spannender. du sagst apply() dies, und apply() das und es passieren 2 unterschiedliche dinge.

    Ist für mich jetzt alles durchgewürfelt. Muß man unterscheiden:

    vector<int> iv;
    vector<string> sv;
    
    iv.size();
    sv.size();
    

    Ist für mich nicht polymorphie. Sind zwei total unterschiedliche Typen, die nichts gemeinsam haben... außer reinzufällig eine size()-Methode.

    Ich könnte höchstens das ganze so lösen:

    template<class T>
    void foo(T &v)
    {
        v.size();
    }
    

    Ist aber für mich einfach nur Typersetzung. Der COmpiler muß nicht entscheiden "Nehme ich jetzt vector<int> oder vector<string>?". Er nimmt das, was ich ihm sage:

    foo(iv);              // nimm implizit vector<int>
    foo<vector<int>>(iv); // nimm explizit vector<int>
    

    Aber das implizit ist ja nur ne Erleichtung für mich. Wirklich entscheiden muß er nicht. Weil es gibt in diesem Beispiel keine Hirarchie und somit keine Vielfältigkeit und somit keine Polymorphie.

    Für Polymorphie muß es ja einen Basistypen geben, von dem es mehrere vererbte Typen gibt:

    // Pseudo code!!!
    
    class base;
    class typ_a : public base;
    class typ_b : public base;
    
    template<class if (1) type_a else if (2) type_b>
    void foo()
    { ... }
    
    int main()
    {
        foo<1+1>(); // Compiler muß hier erstmal wirklich rechnen um nach einem
                    // vielfältigen Typ zu entscheiden == Early Binding
    
        foo<type_a>(); // Compiler setzt einfach den Typ == Typisierung
    }
    

    Da ich (wie bereits im ersten Post gesagt) mich nicht mit Metaprogrammierung auskenne, habe ich einfach mal Pseudocode hingeschrieben! 😉 Aber ich denke mal, ihr könnt meinem Gedanken folgen?

    Das was ihr mir bisher erzählt habt, war für mich einfach nur stupides Typisieren.



  • Artchi schrieb:

    Da ich (wie bereits im ersten Post gesagt) mich nicht mit statischer Polymorphie auskenne, habe ich einfach mal Pseudocode hingeschrieben! 😉 Aber ich denke mal, ihr könnt meinem Gedanken folgen?

    Das was ihr mir bisher erzählt habt, war für mich einfach nur stupides Typisieren.

    Sehr simples Beispiel das meiner Meinung nach weitergeht (Wenn ich mich richtig erinnere war im Modernes C++ Design ein besseres Beispiel, kann es aber auch mit einem Anderen Buch sein).

    class A
    {
      public:
        bool foo();
        void foo2();
    };
    
    class B
    {
      public:
        void foo(int a=0);
    };
    
    template<T>
    class C : private T // Komposition etc. wäre ebenso möglich, ich wähle mal normale Vererbung
    {
      public:
        // Erfordert eine Schnittstelle auf die man ein foo() aufrufen kann
        // Gilt für A und B (trotz unterschiedlicher Signatur)
        void fooT()
        {
          foo();
        }
        // Erfordert eine Schnittstelle auf die man ein foo(), foo2() aufrufen kann
        // Gilt für A
        void foo2T()
        {
          foo();
          foo2();
        }
    };
    
    int main()
    {
      C<A> a;
      a.fooT();
      a.foo2T();
    
      C<B> b;
      b.fooT(); // Solange kein foo2T() aufgerufen wird, erfüllt C<B> die
                // Schnittstelle, sonst: Compilerfehler
    }
    

    cu André



  • Mein Beispiel war nicht gerade sehr sinnvoll. Vorallem die unterschiedlichen Schnittstellen haben gefehlt. Ich konnte mich nur noch dunkel an den inhalt von MCD erinnern. Aber dein Beispiel macht genau das was ich unter statische Polymorphie verstehe!!!

    In MCD waren noch viel mehr Beispiele und Patterns (die deutlicher komplexer waren!), wo ich am Ende nichts mit in der Praxis anfangen konnte.


Log in to reply