Problem mit Vererbung und Polymorphie



  • Gegeben folgender Klassenstammbaum:

    S   T
      / \ /
     P   N
    

    Ich will nun folgendes realisieren:

    1. Suche in einem ptr_vector<S> mit max_element nach dem größten Element. Dabei sollen nur solche Elemente aus dem vector untersucht werden, die ein N und damit ein T sind. Das größte Element soll als T angegeben werden.
    2. Suche nun alle Vorkommnisse von dem größten Elemten im vector, wieder nur unter den N's.

    Kann man das sinnvoll ohne RTTI realisieren?



  • ich bins schrieb:

    Kann man das sinnvoll ohne RTTI realisieren?

    Du willst zur Laufzeit einen Typ Identifizieren - dh du brauchst eine Laufzeit Typ Identifizierung (Runtime Type Identification). Also nein.

    Du kannst aber zB garnicht erst die Typen mischen die nicht gemischt werden sollen... Oder natürlich jedem Typen eine ID mitgeben und die abfragen.

    idR zeigt dies einen Designfehler an wenn man sowas braucht - nicht immer, ist aber ein guter Indiz.



  • Ohne RTTI heisst meist mit virtuellen Funktionen. Mir schwebt da eine Methode vor, die einen für die Ordnung relevanten Wert zurückgibt.

    virtual int S::GetValue() const = 0;
    

    Dabei kannst du die Implementierungen in P so schreiben, dass ein sicher kleinerer Wert als unter N herauskommt.

    virtual int P::GetValue()
    {
        return -1; // Falls N::GetValue() immer ints >= 0 zurückgibt.
    }
    

    Die virtuelle Funktion GetValue() wird dann von std::max_element() aufgerufen, um das Maximum zu finden. Schau dir dazu den dritten Parameter des STL-Algorithms an.

    Allerdings ist std::max_element() etwas ungeeignet, da es dir nur ein Maximum zurückgibt, und nicht alle Vorkommen. So musst du den Container mehrmals durchlaufen, zusätzlich mit std::find() . Die Alternative wäre, selbst eine Maximumsfunktion zu schreiben, die Indizes/Zeiger/Iteratoren auf alle maximalen Elemente in einem std::vector speichert und diesen zurückgibt.



  • Mir ging auch so etwas, wie bei Nexus durch den Kopf, aber sonst stimme ich Shade zu.



  • Ich sage mal genauer, worum es geht: Ich schreibe ein Musikprogramm:

    Satzzeichen   Tonhoehe
          /    \ /
       Pause   Note
    

    Und ich definiere

    typedef boost::ptr_vector<Satzzeichen> Stimme;
    

    Jetzt will ich herausfinden, ob in einer Stimme die höchste Note (d.h. ignoriere Pausen) mehrfach vorkommt.

    Nexus' Idee wäre ja, der Pause eine Tonhoehe = -10000 zuzuordnen. Geht das eleganter?



  • Mir fällt nur gerade auf, dass die Vererbung

    Tonhoehe
      /
    Note
    

    unklug ist. Eine Note hat eine Tonhöhe, ist aber keine.

    Ich weiss nicht, wie viel eleganter dein Problem gelöst werden kann, ohne Shade Of Mines Vorschlag zu berücksichtigen.

    @ drakon: Das sagt uns nun sehr viel...



  • Hat noch jemand eine andere Idee?


  • Mod

    ich bins schrieb:

    Hat noch jemand eine andere Idee?

    Hör auf Nexus. Dein Problem liegt in vermurkstem Design. Anstatt dies jetzt mit diesem Design irgendwie hinzufrickeln solltest du lieber noch einmal über die Beziehungen zwischen deinen Klassen nachdenken. Wenn das Design gut ist, dann sind dieses und andere Probleme viel einfacher zu lösen.



  • Ich hab ja schon nach Nexus' Kritik meine Vererbungshierarchie geändert. Aber das hat das Problem noch nicht gelöst.



  • Nexus schrieb:

    @ drakon: Das sagt uns nun sehr viel...

    Das soll heissen, dass ich die gleiche Idee schreiben wollte, dann aber gesehen habe, dass du das genau so machst und meinen Post wieder entfernt. Ich hätte auch die gleiche Idee nochmal beschreiben können, was mir aber ein wenig unsinnig erscheint, darum die Zustimmung, dass ich das gleiche vorgeschlagen hätte, aber dass ich grundsätzlich Shades Meinung bin, dass da wohl etwas mit dem Design falsch läuft.



  • Nexus schrieb:

    Eine Note hat eine Tonhöhe, ist aber keine.

    hat vielleicht eine Tonhöhe. Schlagzeugnotation ist da sowas was ziemlich aus der Reihe tanzt. Und in gewissem Maße auch der Generalbass.



  • Wenn das Originaldesign beibehalten werden würde (was ich dir nicht empfehle), könntest du vermutlich sowas basteln:

    bool is_T_derived(const T&) { return true; }
    bool is_T_derived(...) { return false; }
    
    struct Pred
    {
        bool operator<(const U& lhs, const U& rhs)
        {
            if(!is_T_derived(lhs))
                 return is_T_derived(rhs);
            return is_T_derived(rhs) && lhs.tonhoehe < rhs.tonhoehe;
        }
    };
    


  • Inwiefern brauchst du eigentlich die Klasse "Tonhöhe" in deinem Design? Wäre es nicht klüger, wenn du für deine "musikalischen Objekte" (also Noten, Pausen, etc.) eine Methode play() definieren würdest? So hättest du auch gleich die Pausen, Schlagzeugnoten oder was auch immer erschlagen.



  • Du bist viel flexibler, wenn du "spielbare" und "unspielbare" Elemente trennst. Ob du nun eine Midi-Datei generieren willst, das Stück abspielen, den Takt transponieren oder einfach nur nach dem höchsten Ton suchen willst, du musst andauernd die Pausen von den Noten trennen.
    Die Pausen bekommen eine zweite Liste und die dritte Liste ist für Satzzeichen wie Bindebögen, Punktierungen, Generalbass und Text.
    Wenn es dich stört, mehrere Listen zu verwenden, musst du halt einen TonhöhenIterator schreiben, der verdeckt typeof aufruft. Aber das ist mehr Aufwand wie die Trennung der Listen.

    @Michael E.: Rat mal was bei is_T_derived(*(static_cast<Satzzeichen *>(new Tonhoehe()))); passiert.



  • BachfugenFan schrieb:

    @Michael E.: Rat mal was bei is_T_derived(*(static_cast<Satzzeichen *>(new Tonhoehe()))); passiert.

    Was willst du mir sagen? 😕

    error C2440: 'static_cast': 'Tonhoehe *' kann nicht in 'Satzzeichen *' konvertiert werden
    Die Typen, auf die verwiesen wird, sind nicht verknüpft; die Konvertierung erfordert einen reinterpret_cast-Operator oder eine Typumwandlung im C- oder Funktionsformat.
    

Log in to reply