Contra dynamic_cast?



  • Welchen Grund könnte man haben, die Funktionalität von dynamic_cast über virtuelle Funktionen auszudrücken? Mir fallen erstmal nur eindeutige Nachteile ein: Abhängigkeit der Basisklasse von den Kindern, hohe Fehleranfälligkeit. Gibt es irgendwelche Vorteile? Performance, vielleicht Probleme mit DLLs umgehen? Irgendwas?



  • Ich weiß nicht, wie Du darauf kommst, daß die Funktionalität von dynamic_cast nachgebildet werden soll. Ich vermute, daß Du da was falsch verstanden hast.

    Dynamic_cast soll man verwenden, wenn die Funktionalität benötigt wird. Es ist nur häufig so, daß die Notwendigkeit der Verwendung auf einen Designfehler hindeutet. Wenn es wirklich notwendig ist, sollte man sich überlegen, ob das Design stimmt. Bei einem guten Design ist dynamic_cast einfach sehr selten wirklich notwendig.

    Tntnet



  • tntnet schrieb:

    Ich weiß nicht, wie Du darauf kommst, daß die Funktionalität von dynamic_cast nachgebildet werden soll. Ich vermute, daß Du da was falsch verstanden hast.

    Nein, nur nicht dran gedacht, dass man meine Frage falsch verstehen könnte 😉
    Ich meine folgende Konstruktion:

    if (Derived* d = base->isDerived())  // dynamic_cast<Derived*> für Arme
      ...
    

    wobei isDerived eine virtuelle Funktion ist, die in Base 0 und in Derived this zurückgibt.



  • Bashar schrieb:

    Welchen Grund könnte man haben, die Funktionalität von dynamic_cast über virtuelle Funktionen auszudrücken? Mir fallen erstmal nur eindeutige Nachteile ein: Abhängigkeit der Basisklasse von den Kindern, hohe Fehleranfälligkeit. Gibt es irgendwelche Vorteile? Performance, vielleicht Probleme mit DLLs umgehen? Irgendwas?

    So wirkliche Vorteile wüsste ich spontan auch nicht, aber:

    - Die Abhängigkeit in der Basisklasse ist nicht wirklich sooo furchtbar dramatisch, je eine forward-declaration pro Klasse; so was wird wohl auch nur vorkommen wenn die Klassen fest zusammen gehören und sich wahrscheinlich ohnehin kennen.
    - Die hohe Fehleranfälligkeit sehe ich so nicht, was meinst du damit?
    - Kürzer und leichter lesbar als dynamic_cast .
    - Performanter kann ich mir gut vorstellen. Hängt sicher von der Implementierung ab, aber schneller als ein einziger virtueller Methodenaufruf wird dynamic_cast eher nicht sein.
    - Man hat den Cast als Methode zur Verfügung (keine Ahnung ob sich dafür ein sinnvoller Anwendungsfall konstruieren lässt).



  • Bashar schrieb:

    tntnet schrieb:

    Ich weiß nicht, wie Du darauf kommst, daß die Funktionalität von dynamic_cast nachgebildet werden soll. Ich vermute, daß Du da was falsch verstanden hast.

    Nein, nur nicht dran gedacht, dass man meine Frage falsch verstehen könnte 😉
    Ich meine folgende Konstruktion:

    if (Derived* d = base->isDerived())  // dynamic_cast<Derived*> für Arme
      ...
    

    wobei isDerived eine virtuelle Funktion ist, die in Base 0 und in Derived this zurückgibt.

    Ich denke schon, daß ich Dich richtig verstanden habe. Das ist genau das was ich meine.

    Eine Methode "isDerived" macht keinen Sinn. Dafür gibt es dynamic_cast. Es deutet aber auf ein Designfehlen an, wenn ich überhaupt wissen muß, welchen aktuellen Typ die Klasse hat. Statt:

    if (Derived* d = base->isDerived())  // dynamic_cast<Derived*> für Arme
    { /* mache was Derived-spezifisches */ }
    

    Wäre das besser:

    base->doSomething(); // liebe Klasse: mach, was Du für nötig hälst
    

    doSomething ist hier eine virtuelle Methode, die in Base nichts macht und in Derived überladen wird.

    Tntnet



  • Das einzige was ich mir vorstellen koennt:
    - Dynamic_cast braucht RTTI ... RTTI macht overhaed.
    - Sind eigentlich klassensignaturen mit RTTI und ohne RTTI noch kompatibel zueinander ? (wenn exe und dll mit unterschiedlichen RTTI einstellungen compiliert wurden, und die dll klassen exportiert, koennt es vielleicht probleme geben)
    - RTTI kann peroformance probleme bringen, Glaub VC6 war ziemlich verrufen in der beziehung, weiss ned wie es mit dem neuen compiler ist.

    Ciao ...



  • @Bashar
    Neben den technischen Vorteilen wie bessere Performanz (häufig zweifelhaft, da man ja nun nicht sooft einen dynamic_cast verwendet), kleinerer Code (wieviel RTTI extra kostet ist sicher abhängig vom Compiler), scheint mir das Ganz vorallem im manchen Situationen hübscher zu sein. Wenn man z.B. eine feste Hierarchie hat und man häufig zwischen konkreten abgeleiteten Klassen hin und her wandert, dann ist eine eindeutig benannte Funktion (angeblich) hübscher als ein dynamic_cast (Bsp: XML-Libraries). Wobei ich gestehen muss, dass ich kein Fan von dieser Technik bin.

    Sind eigentlich klassensignaturen mit RTTI und ohne RTTI noch kompatibel zueinander ? (wenn exe und dll mit unterschiedlichen RTTI einstellungen compiliert wurden, und die dll klassen exportiert, koennt es vielleicht probleme geben)

    Natürlich gibt das Probleme. Das gilt aber generell für "unterschiedliche Einstellungen". Schon die Verwendung einer virtuellen Funktion kann schief gehen, wenn Programm und dll nicht mit dem selben Compiler und den selben Einstellungen erzeugt wurden.



  • dynamic_cast ist - zumindest mit VC6/VC7.1/VC8 - relativ langsam. Je tiefer die Klassenhierarchie, desto schlimmer. Auf jeden Fall um Grössenordnungen langsamer als ein oder zwei virtual calls.
    p.S.: über andere Compiler kann ich leider nix sagen, da hab ich's nicht ausprobiert.



  • Schon die Verwendung einer virtuellen Funktion kann schief gehen, wenn Programm und dll nicht mit dem selben Compiler und den selben Einstellungen erzeugt wurden.

    Schon klar, aber bei Abstracten klassen sind "normal" eh nur das namemergling und die anordnung der Vtable nen problem, bei gleichen compilerversionen ned wirklich nen problem, weiss ned ob die RTTI da was verschiebt.
    Zumindest unter VS machen abstrakte klassen selber keine probleme sogar bei debug und release versionen.
    Standardisiert ist da natuerlich gar nix mehr, also faellt eh alles unter kann, muss aber nicht. Standardisiert sind nur C schnittstellen, wo RTTI ja soweiso rausfaellt ^^

    Ciao ...



  • tntnet schrieb:

    Ich denke schon, daß ich Dich richtig verstanden habe. Das ist genau das was ich meine.

    Nein, ich habe keine Designfrage gestellt. Sinn und Unsinn von Downcasts sind mir klar, es geht um die Implementierung.

    hustbaer schrieb:

    dynamic_cast ist - zumindest mit VC6/VC7.1/VC8 - relativ langsam. Je tiefer die Klassenhierarchie, desto schlimmer. Auf jeden Fall um Grössenordnungen langsamer als ein oder zwei virtual calls.

    Komisch eigentlich, oder? Der macht doch eigentlich in solche Fällen auch nicht mehr als die virtuelle Funktion? Mal unter der Annahme dass weder virtuelle noch Mehrfachvererbung vorliegt.



  • Bashar schrieb:

    tntnet schrieb:

    Ich denke schon, daß ich Dich richtig verstanden habe. Das ist genau das was ich meine.

    Nein, ich habe keine Designfrage gestellt. Sinn und Unsinn von Downcasts sind mir klar, es geht um die Implementierung.

    hustbaer schrieb:

    dynamic_cast ist - zumindest mit VC6/VC7.1/VC8 - relativ langsam. Je tiefer die Klassenhierarchie, desto schlimmer. Auf jeden Fall um Grössenordnungen langsamer als ein oder zwei virtual calls.

    Komisch eigentlich, oder? Der macht doch eigentlich in solche Fällen auch nicht mehr als die virtuelle Funktion? Mal unter der Annahme dass weder virtuelle noch Mehrfachvererbung vorliegt.

    Nein, macht er nicht, er macht strcmp() in einer Schleife. Step einfach mit dem Debugger rein, dann siehste es ja 😉



  • Bashar schrieb:

    tntnet schrieb:

    Ich denke schon, daß ich Dich richtig verstanden habe. Das ist genau das was ich meine.

    Nein, ich habe keine Designfrage gestellt. Sinn und Unsinn von Downcasts sind mir klar, es geht um die Implementierung.

    hustbaer schrieb:

    dynamic_cast ist - zumindest mit VC6/VC7.1/VC8 - relativ langsam. Je tiefer die Klassenhierarchie, desto schlimmer. Auf jeden Fall um Grössenordnungen langsamer als ein oder zwei virtual calls.

    Komisch eigentlich, oder? Der macht doch eigentlich in solche Fällen auch nicht mehr als die virtuelle Funktion? Mal unter der Annahme dass weder virtuelle noch Mehrfachvererbung vorliegt.

    OK - sehe ich ein.

    Ich würde dennoch jederzeit dynamic_cast bevorzugen, wenn denn so eine Funktionalität wirklich benötigt wird. Wenn ein bestimmter Compiler in einer bestimmten Version da ein Performanceproblem hat, ist das für mich kein Argument, es nicht zu verwenden, solange ich die Stelle nicht als definitiven Performanceengpass identifiziert habe. Es steigert einfach die Lesbarkeit des Codes, wenn ich die vorgesehenen Sprachkonstrukte verwende statt mir irgendetwas eigenes auszudenken.

    Tntnet



  • Vorallem: was ist wenn in der nächsten Compiler-Version eine hyper duper dynamic_cast-optimierte Implementierung rein kommt? Dann wieder den ganzen Code absuchen um alles durch dynamic_cast zu ersetzen? Ich benutze auch lieber das, was "richtig" ist bzw. was von der Sprache vorgesehen wurde.



  • Wie ist IsDerived eigentlich definiert?
    Kann eine virtuelle Funktion verschiedene Rückgabetypen haben?



  • tntnet schrieb:

    Ich würde dennoch jederzeit dynamic_cast bevorzugen, wenn denn so eine Funktionalität wirklich benötigt wird.

    Ich auch.

    phlox81 schrieb:

    Wie ist IsDerived eigentlich definiert?
    Kann eine virtuelle Funktion verschiedene Rückgabetypen haben?

    äquivalent zu dynamic_cast:

    Derived* Base::isDerived() { return 0; }
    Derived* Derived::isDerived() { return this; }
    

    Ich glaube mich zu erinnern, dass Robert C. Martin so eine Technik in "Objektorientierte C++ Anwendungen: Die Booch- Methode" verwendet. Das Buch ist aber auch ziemlich alt, damals konnte noch nicht jeder Compiler dynamic_cast.



  • Komisch eigentlich, oder? Der macht doch eigentlich in solche Fällen auch nicht mehr als die virtuelle Funktion? Mal unter der Annahme dass weder virtuelle noch Mehrfachvererbung vorliegt.

    Der Compiler sieht bloss ich habe nen Foo* und will einen Bar* haben. Gehen wir mal davon aus dass so etwas wie DLLs oder "shared objects" weiterhin möglich sein soll, dann kann der Compiler garnicht wissen dass so ein einfacher Fall vorliegt.

    Er kann beim Compilieren der EXE (die in diesem Beispiel den dynamic_cast enthält) garnicht nicht wissen ob es nicht in irgendeiner DLL eine Klasse gibt die (u.U. auch mehrfach) von Foo ableitet, ob diese Klasse eventuell virtuelle Vererbung verwendet oder nicht, ob sie dann ein Bar enthält (oder auch mehrere) und wo diese Bars in der Hierarchie auftauchen etc.

    Genausowenig kann der Compiler beim Compilieren der DLL wissen dass die EXE einen Haufen dynamic_casts von Foo* auf Bar* macht - u.U. ist beim compilieren der DLL nur Foo bekannt und Bar sieht der Compiler garnicht.

    Ich frage mich ernsthaft wie in so einem Fall eine vernünftige Optimierung möglich sein soll. (Ok, mit JIT Systeme wäre es möglich, aber das mal aussen vor)

    Profile guided optimization wäre eine Möglichkeit - allerdings ist das halt sehr mühsam, und umso mühsamer je flexibler die Einsatzmöglichkeiten eines Programms sind und je unvorhersehbarer die Eingabedaten.

    Davon abgesehen bin ich auch dafür den "natürlichen Weg" zu nehmen, was eben je nach Situation typeid, dynamic_cast oder ein virtual call sein kann.

    An Stellen die sehr performance-kritisch sind, und wo es einen deutlichen Unterschied macht, werde ich aber weiterhin dynamic_cast meiden wie die Beulenpest, auch wenn das ein paar "unschöne Verrenkungen" erfordert. Selbiges gilt für dynamische Speicheranforderungen, interlocked Befehle, etc.



  • Der Knoten ist geplatzt glaub ich 💡
    Der Compiler hat ja nicht den Luxus, den sich der Proponent der Alternativtechnik leistet, alle abgeleiteten Klassen schon in der Basisklasse zu kennen. Nachträglich versteckte virtuelle Funktionen, also auch Einträge in die vtable, hinzuzufügen dürfte theoretisch gehen, ist aber wohl zu aufwändig.


Anmelden zum Antworten