Feststellen, ob Typ des Objekt die Ableitung eines anderen ist



  • Hallo,

    folgendes Problem habe ich:

    Es muss zur Laufzeit festgestellt werden, ob ein Objekt eine Ableitung eines anderen Objektes ist.
    (Anmerkung: Das ist keine Verwechslung zwischen Klasse und Objekt)

    Beispiel

    class A {
    virtual void dummy(void) {};  //braucht RTTI
    };
    class A1  : A {};
    class A11 : A1 {};
    class A2  : A {};
    
    //Objekte davon erzeugen
    A* a=    new A();
    A* a1=   new A1();
    A* a11=  new A11(); 
    A* a2=   new A2();
    
    //Und nun sollte der Verlgeich kommen
    if ( ableitung_von(a, a1) )   ---> true
    if ( ableitung_von(a, a11) )  ---> true
    if ( ableitung_von(a2, a11) ) ---> false
    

    Habt ihr vielleicht eine Idee, wie ich die Funktion ableitung_von() implementieren koennte?

    Danke.



  • dynamic_cast



  • #include <typeinfo>
    
    ...
    if (typeid(A).before( typeid(*a1) )) // true
    if (typeid(A).before( typeid(*a11) )) // true
    if (typeid(A2).before( typeid(*a11) )) // false
    
    /* Für A selbst */
    if (typeid(A) == typeid(*a)) // true
    

    So gehts. Ob das empfohlen ist, das so zu machen, würd mich jetzt selbst mal interessieren 🙂



  • @LordJaxom: Ich möchte meinen, dass das nicht definiert ist, da before nur eine totale Ordnung alle möglichen Typen bereitstellt, die aber nichts mit ihrer Vererbungshierarchie zutun haben muss.

    dynamic_cast ist schon die richtige Lösung für die Problemstellung.

    C++-Standard 18.5.1 Cl. 4:
    bool before(const type_info& rhs) const;
    5 Effects: Compares the current object with rhs.
    6 Returns: true if *this precedes rhs in the implementations collation order.

    Wobei "implementations collation order" keine Aussage über die Typ-Beziehungen macht. before kann auch true liefern für Typen, die in keinerlei Beziehung zueinander stehen.



  • Danke zunächst mal.

    An dynamic_cast dachte ich auch schon, aber damit kann ich nach meinem Wissen keine zwei Objekte vergleichen:

    die normale Anwendung ist:

    dynamic_cast<A1*>(a1)   --> true
    dynamic_cast<A1*>(a11)  --> true
    dynamic_cast<A1*>(a2)   --> false
    

    Problematisch ist aber hierbei, dass ich so damit nur ein Objekt gegen einen zur Compilezeit festgelegten Typ testen kann und nicht zwei Objekte zur Laufzeit.

    Wie würdet ihr das machen?



  • Ohne mir jetzt Gedanken über die Machbarkeit gemacht zu haben: Wozu brauchst du das? 😮 Das klingt eher nach einem schlechten Design.



  • wenn du 2 Objekte hast und testen willst, ob sie zur selben Klasse gehören, wirst wohl RTTI benutzen müssen, da kommst an die Klassennamen ran. Aber komisch klingt das schon, was du willst. Lässt sich das nicht umgehen?



  • Es geht ja nicht darum festzustellen, ob zwei Objekte die selbe Most-Derived-Class haben (was auch unschön ist, sich aber über den Vergleich von type_infos machen lässt), sondern welches von den beiden "more derived" ist (was imho nicht sinnvoll ist bzw. ein Missverständnis/Missbrauch von OOP - deshalb interessiere ich mich für die Absicht des OP). Ich möchte meinen dass das mit den Standardmitteln der C++ Sprache nicht möglich ist, da sie keinen Laufzeitmechanismus bereitstellt, um über die Vererbungshierarchie zu iterieren. Wenn das wirklich sein muss (was ich bezweifle), muss man sich das wohl händisch stricken.



  • markus.r schrieb:

    Problematisch ist aber hierbei, dass ich so damit nur ein Objekt gegen einen zur Compilezeit festgelegten Typ testen kann und nicht zwei Objekte zur Laufzeit.

    dynamic_cast prüft zur Laufzeit, nicht zur Compilezeit, deswegen dynamic_cast. 🙂

    Also dein Bsp oben kriegst du ja locker hin damit.

    Wenn du dagegen das willst, was 7H3 N4C3R im Post vor mir geschrieben hat, also feststellen welche mehr abgeleitet ist oder solche Späße. da bringt das natürlich nix.



  • Danke erstmal.

    dann komme ich wohl um folgendes Konstrukt nicht herum:

    if ( (dynamic_cast<A*>(p_objekt1)) && (dynamic_cast<A*>(p_objekt2) ) return true
    else if ( (dynamic_cast<A1*>(p_objekt1)) && (dynamic_cast<A1*>(p_objekt2) ) return true
    else if ( (dynamic_cast<B*>(p_objekt1)) && (dynamic_cast<B*>(p_objekt2) ) return true
    //..
    return false;
    


  • [quote="Pellaeon"]

    markus.r schrieb:

    dynamic_cast prüft zur Laufzeit, nicht zur Compilezeit, [..] dynamic_cast. 🙂

    dynamic_cast<Argument1>(Argument2);
    

    Zur Laufzeit ist lediglioch Argument2 variabel, Argument1 muss zur Compilezeit statisch festgelegt werden.

    In meinem Fall muss ich jedoch zwei Objekte vergleichen, deren Typen zur Laufzeit erst feststehen.
    Also ich möchte wissen, ob die beiden Typen der Objekte einen gemeinsamen Vorgänger in der Vererbungshierarchie haben.



  • Diese Lösung ist nicht nur "unästhetisch", sondern bricht auf mit objektorientierten Prinzipien. Sie ist kaum wartbar, da jede Veränderung in der Vererbungshierarchie quasi ein Neustricken der Funktion bedeutet. Ohne mir intensiv Gedanken gemacht zu haben würde ich sagen, hat eine Funktion dieser Art exponenzielle Komplexität - sowohl bei Auswerten als auch beim Ausprogrammieren. Absoluter Overkill.

    Sofern du Ratschläge oder alternative sauberere Lösungen suchst, erklär uns doch bitte mal das Problem und was du vorhast.

    Die Ironie an deinem zuletzt geposteten Code ist übrigens, dass du alle Typen statisch benannt hast.



  • Schreib doch mal, was du gerade genau programmieren willst, dann kann man dir mehr helfen. Das was du da programmieren willst, ist nicht wirklich eine Option^^



  • 7H3 N4C3R:
    Richtig, daher fragte ich auch, denn auf die schlechte Lösung mit den dynamic_cast bin ich selbst schon gekommen.

    Das Problem lässt sich wie folgt umreissen.

    Es gibt verschiedene STL-Listen, welche Pointer auf verschiedene Objekte-Arten beinhalten können.
    Hier ein Beispiel mit den Klassen von oben:

    list<A*> liste_a;  
    //gefuellt mit Pointer auf Objekte vom Typ A, A1, A2 und A11
    
    list<A1*> liste_a1;
    //gefuellt mit Pointer auf Objekten vom Typ A1 und A11
    
    list<B*> liste_b;
    //gefuellt mit Pointer auf Objekte vom Typ B1 usw.
    

    Objekte sollen nun zwischen den Listen wahlfrei verschoben werden können, wenn das der Typ zulässt.

    Die anderen Probleme, die hier auftreten, lassen wir mal aussen vor.



  • da würde doch jeweils ein cast reichen oder?
    testen ob der neue Zeiger zum Zeigertyp der Liste passt.



  • Bisher haben alle Deine Beispiele die Eigenschaft dass es nur eine Basisklasse (A) gibt. D.h. es macht IMHO bisher keinen Sinn etwas anderes als nur eine list<A*> zu halten (oder?).

    Ansonsten, wenn Du eine list<A1*> haben musst, weisst Du selbst doch schon dass jedes Element dieser Liste von A abgeleitet ist, und demnach dynamic_cast auch garantiert nicht NULL liefern wird.

    Irgendwie hab ich die Ahnung, dass das eigentlich einfacher gehen muss 😕



  • :xmas2:



  • Pellaeon schrieb:

    da würde doch jeweils ein cast reichen oder?
    testen ob der neue Zeiger zum Zeigertyp der Liste passt.

    So wie ich das sehe nicht. Anscheinend gibt es Ableitungen von T, die aber im Verhalten nicht mehr kompatibel mit T sind (und deshalb in einem Vektor XY unerwünscht). Das ist aber wiederum ein Bruch mit dem Liskov-Substitution-Principle.

    Die Problembeschreibung klingt ein wenig nach einem GUI mit Baumansicht, in der Drag and Drop möglich sein soll - nur eben nicht überall hin. Eine Möglichkeit wäre vielleicht, die Typanforderungen der Vektoren zu erhöhen. Sprich z.B. Vektoren mit Zeigern auf B zu halten, wo auch nur Bs reinsollen. Ein Vektor mit Zeigern auf Bs kann dann immernoch in einen Vektor von Zeigern auf As kopiert werden, sofern B von A abgeleitet ist.



  • Da C++ eine Compilersprache und keine Laufzeitsprache ist, werde ich wohl in den sauren Apfel beissen müssen.

    Eine ähnliche Idee habe ich damals übrigens verworfen, dass alle Klassen von der gleichen Urklasse abgeleitet sind und ich in den Listen quasi nur Pointer auf die Urklasse halte.

    Die sonderbare Listenbildung hat übrigens einen fachlichen Grund.



  • markus.r schrieb:

    list<A*> liste_a;  
    //gefuellt mit Pointer auf Objekte vom Typ A, A1, A2 und A11
    
    list<A1*> liste_a1;
    //gefuellt mit Pointer auf Objekten vom Typ A1 und A11
    
    list<B*> liste_b;
    //gefuellt mit Pointer auf Objekte vom Typ B1 usw.
    

    Objekte sollen nun zwischen den Listen wahlfrei verschoben werden können, wenn das der Typ zulässt.

    liste_a und liste_a1 in liste_b würde gar net gehen, Compilefehler, esseidenn du würdest wie wild casten, was du ja nicht machst.

    liste_a1 nach liste_a klappt auch ohne Probs. liste_b in eine liste_a* würde auch net kompilieren, also bleibt doch nur liste_a nach liste_a1. Und da würd ein dynamic_cast helfen.
    Insofern halt wirklich nur die Typen ausschlaggebend sind und nichts anderes

    edit: ich seh grad ich vergaß die Basisklasse die alle gemein haben^^
    Aber dynamic_cast müsste gehen, insofern man an der Struktur mit den Listen nichts ändern kann



  • 7H3 N4C3R schrieb:

    Das ist aber wiederum ein Bruch mit dem Liskov-Substitution-Principle.

    Wenn ich das richtig verstanden habe, dass das "Liskov-Substitution-Principle" auch als "ist-ein"-Regel bekannt ist, dann stimme ich zu, dass ich die Klasse B etwas voreilig in das Beispiel eingefuehrt.
    In dem realen Projekt ist das selbstverstaendlich nicht der Fall.

    Ich veranschauliche das Problem mal anders.
    Es geht hier um das Sortieren von Autos.

    class Fahrzeug {};
    class Sportwagen : Fahrzeug {};
    class Porsche : Sportwagen {};
    class Ferrari : Sportwagen {};
    class Traktor : Fahrzeug {};
    
    //und nun die Listen
    list<Fahrzeug*> parkplatz;
    list<Sportwagen*> parkplatz_golfplatz;
    list<Porsche*> prosche_haendler;
    list<Traktor*> acker;
    

    7H3 N4C3R schrieb:

    Die Problembeschreibung klingt ein wenig nach einem GUI mit Baumansicht, in der Drag and Drop möglich sein soll

    Exakt das ist eine Teilanwendung davon, sehr gut "hellgesehen":-)


Anmelden zum Antworten