C-Cast in C++



  • Nur C-Cast?
    Kann man dann überhaupt alles implementieren?
    Nen C-Cast macht doch nie nen dynamic_cast, oder?



  • Nö, er reinterpretiert. Das langt.



  • SeppSchrot schrieb:

    Nö, er reinterpretiert. Das langt.

    falsch



  • Hallo,

    ChefCaster schrieb:

    Verwendet ihr beim Casten von einfachen Datentypen ein
    C-Cast oder ein static_cast?

    ich verwende nur die C++ Casts. Nicht nur, dass man auch weiß, wie gerade gecastet wird, man sieht den Cast im Source auch besser als den C-Cast.

    MfG

    GPC



  • ChefCaster schrieb:

    Nen C-Cast macht doch nie nen dynamic_cast, oder?

    Das ist richtig, allerdings brauchst du dynamic_cast so gut wie nie. Leider verwenden viele Leute den Cast zu leichtfertig und unbedacht. Dennoch, C-Casts sollte man deswegen trotzdem nicht verwenden.

    ChefCaster schrieb:

    Verwendet ihr beim Casten von einfachen Datentypen ein
    C-Cast oder ein static_cast?

    || 
     || 
     || 
    \  /
     \/  :D
    


  • Wann brauch man denn einen dynamic_cast?
    Kann mir einer mal nen Beispiel geben, wo ein static_cast nicht ausreicht...

    Ich z. Bsp. dachte immer, wenn ich ne Klasse zu einer
    anderen Klasse caste, die über die Vererbunghierarchie keine
    direkte Verküpfung haben:

    class A{
    }
    
    class C {
    }
    
    class B : public C{
    }
    

    Also hier, wenn ich von A nach C caste oder umgekehrt...

    Gruß,
    dyncast



  • Ne, so funktioniert das nicht. dynamic_cast kannst du auch nur in der Vererbungshierarchie verwenden. Genauer gesagt, man verwendet dynamic_cast eigentlich nur für den Downcast. Und auch nur dann, wenn du dir nicht sicher bist, von welcher abgeleiteten Klasse das Objekt ist. Ansonsten reicht für den Downcast auch static_cast. Ein sinnvolles Beispiel für dynamic_cast kann ich dir leider nicht geben, da ich versuche, es schlichtweg zu vermeiden. Und das hat bis jetzt auch ganz gut geklappt. Was aber nicht heissen soll, dass es keine sinnvollen Verwendungen für dynamic_cast gibt.



  • Für cross-casts braucht man den dynamic-cast unbedingt:

    class A;
    class B : public A;
    class C : public A;
    class D : public B,C

    Der dynamic-cast kann jetzt einen Pointer vom Typ B*, der auf ein Objekt der Klasse D zeigt korrekt in einen Pointer vom Typ C* umwandeln. Das kann der static_cast garnicht können.

    Ob man sowas wirklich braucht ist ne andere Frage. Mir ist es noch nicht begegnet.



  • Fehler schrieb:

    SeppSchrot schrieb:

    Nö, er reinterpretiert. Das langt.

    falsch

    Nö, nur teilweise falsch. Ein C-Cast macht je nach Kontext einen static_, einen const_ und/oder einen reinterpret_cast (aber nie dynamic). Sind für einen static_cast nicht genug Informationen vorhanden, so wird einfach ein reinterpret_cast gemacht, ohne dass man das dem Code ansehen könnte oder eine Compiler-Warnung ausgegeben wird. Wird beispielsweise ein Zeiger in einer Klassenhierarchie hoch- und runter-gecastet (Sinnhaftigkeit steht mal nicht zur Diskussion) und es stehen nur Forward-Deklarationen zur Verfügung (sprich, die Vererbungsbeziehung ist an dieser Stelle vor'm Compiler versteckt), so würde der static_cast einen Compiler-Fehler verursachen, weil er das Offset für den Zeiger nicht ausrechnen kann. Der C-Cast hingegen würde dann einfach einen reinterpret_cast machen, weil der static_cast nicht klappt. Dass das fatale Konsequenzen hat (und ziemlich unmöglich zu findende Fehler), sollte klar sein.

    Das gefährliche am C-Cast ist eben genau, dass er je nach Kontext seine Semantik ändert. Dagegen haben die C++-Casts eine eindeutige Semantik und schlagen fehl, wenn das Gewünschte nicht klappt.



  • @Jester:
    Welche Rolle spielt A in deinem Beispiel?

    btw:
    Brauch man für ein "instanceof" nicht einen dynamic_cast?



  • "instanceof" ist genau die Situation "Genauer gesagt, man verwendet dynamic_cast eigentlich nur für den Downcast. Und auch nur dann, wenn du dir nicht sicher bist, von welcher abgeleiteten Klasse das Objekt ist." (Zitat groovemaster) 😉

    Und ein "instanceof" ausserhalb irgendeiner Vererbungshierarchie (a la "dieses void* ist ein std::string*") geht sowieso nicht und würde auch keiner machen wollen 😃



  • 7H3 N4C3R schrieb:

    Fehler schrieb:

    SeppSchrot schrieb:

    Nö, er reinterpretiert. Das langt.

    falsch

    Nö, nur teilweise falsch. [...]

    Nö, die Aussage ist falsch.



  • Jungs ich will ja nix sagen aber das mit dem Unterschied zwischen dynamic_cast und static_cast solltet ihr euch wirklich nochmal in einem guten Buch reinziehen...

    Der dynamic_cast ist eine Überprüfung zur Laufzeit. Der static_cast ist eine Überprüfung zur Compilierzeit.

    Gerade wenn ihr mit Schnittstellen arbeitet und während das Programm läuft feststellen wollt ob ihr auch das Objekt vom richtigen Typ am Wickel habt (oder überhaupt ein gültiges wenn mal wieder jemand vergessen hat einen Pointer richtig zu initialisieren) dann helfen euch static_cast nicht sonderlich viel.



  • Jester schrieb:

    Für cross-casts braucht man den dynamic-cast unbedingt:

    class A;
    class B : public A;
    class C : public A;
    class D : public B,C

    Der dynamic-cast kann jetzt einen Pointer vom Typ B*, der auf ein Objekt der Klasse D zeigt korrekt in einen Pointer vom Typ C* umwandeln. Das kann der static_cast garnicht können.

    Wieso soll das static_cast nicht können?

    B* foo = new D;
    C* bar = static_cast<D*>(foo);
    

    Oder hab ich dich irgendwie falsch verstanden?

    Mathias schrieb:

    Jungs ich will ja nix sagen aber das mit dem Unterschied zwischen dynamic_cast und static_cast solltet ihr euch wirklich nochmal in einem guten Buch reinziehen...

    Der dynamic_cast ist eine Überprüfung zur Laufzeit. Der static_cast ist eine Überprüfung zur Compilierzeit.

    Und worauf bezieht sich das?
    Btw, Casts sind keine Überprüfungen. dynamic_cast macht das auch nur nebenbei, um ggf eine Exception zu werfen oder einen Nullzeiger zurückzugeben. Und static_cast kann ebenfalls Laufzeit kosten.

    Mathias schrieb:

    Gerade wenn ihr mit Schnittstellen arbeitet und während das Programm läuft feststellen wollt ob ihr auch das Objekt vom richtigen Typ am Wickel habt (oder überhaupt ein gültiges wenn mal wieder jemand vergessen hat einen Pointer richtig zu initialisieren) dann helfen euch static_cast nicht sonderlich viel.

    Das widerspricht sich. Schnittstellen (abstrakte Klassen) sind erstmal generell ein Mittel, um ausgiebige Cast-Orgien zu vermeiden.
    Und dynamic_cast um festzustellen, ob ein Zeiger korrekt initialisiert wurde? Schon klar. 🙄 Du ziehst dir auch die Hosen mit der Kneifzange an, oder?



  • groovemaster schrieb:

    B* foo = new D;
    C* bar = static_cast<D*>(foo);
    

    Oder hab ich dich irgendwie falsch verstanden?

    Und woher weißt du, dass foo ein D ist?
    Ändern wir das Beispiel mal:

    B* foo = myFactory.create(); //Der Typ ist hier unbekannt, nur die Basisklasse B ist bekannt
    //Um herauszufinden welches Objekt man hat, hilft einem nur dynamic_cast
    


  • Das ist ein Fall für static_cast:

    B* foo = new D;
    C* bar = static_cast     <C*> (foo);
    

    Das ist ein Fall für dynamic_cast:

    void Foo (B * pObject)
    {
    C* pTempObject = dynamic_cast <C*> (pObject);
    }
    

    Btw, Casts sind keine Überprüfungen. dynamic_cast macht das auch nur nebenbei, um ggf eine Exception zu werfen oder einen Nullzeiger zurückzugeben. Und static_cast kann ebenfalls Laufzeit kosten.

    Autsch das tut jetzt aber weh.... Natürlich sind Casts keine Überprüfungen aber dies ist der Unterschied zwischen einem C-Cast und static_cast bzw. dynamic_cast. C-Casts sind eine ziemlich unsichere Sache static_casts und dynamic_casts liefern dir eine Rückmeldung obs geklapt hat oder nicht.

    Der static_cast macht die Überprüfung während der Code übersetzt wird. Der dynamic_cast führt zu einer Überprüfung wenn das Programm später verwendet wird.

    Das widerspricht sich. Schnittstellen (abstrakte Klassen) sind erstmal generell ein Mittel, um ausgiebige Cast-Orgien zu vermeiden.
    

    Im Allgemeinen schon, nur ab und zu soll es auch mal vorkommen dass man die Schnittstelle des tatsächlichen Objekttyps braucht und in diesem Fall brauchst du nunmal einen Cast idealerweise mit Typüberprüfung zur Laufzeit (dynamic_cast) und nicht auf gut Glück.

    Und dynamic_cast um festzustellen, ob ein Zeiger korrekt initialisiert wurde? Schon klar.  Du ziehst dir auch die Hosen mit der Kneifzange an, oder?
    

    Wenn ich dadurch einen Programmabsturz vermeiden kann. Es soll Leute geben, die auch Code von Anderen über Schnittstellen ansprechen. Wenn da Müll geliefert wird muss mans irgendwie abfangen mindestens bis das nächste Patch des Fremdcodes draußen ist.



  • Aber schrieb:

    Und woher weißt du, dass foo ein D ist?

    Vielleicht solltest du dir mal den ganzen Thread durchlesen. 😉 Darum ging es nämlich überhaupt nicht. Dass static_cast keine Laufzeitüberprüfungen durchführt, sollte ja bekannt sein. Es ging lediglich um die grundsätzliche Frage, ob es überhaupt möglich ist.

    Mathias schrieb:

    Im Allgemeinen schon, nur ab und zu soll es auch mal vorkommen dass man die Schnittstelle des tatsächlichen Objekttyps braucht und in diesem Fall brauchst du nunmal einen Cast idealerweise mit Typüberprüfung zur Laufzeit (dynamic_cast) und nicht auf gut Glück.

    Dann ist das aber keine gute Schnittstelle. Diese sollte alles bieten, was ich brauche. Ansonsten kann ich auch gleich alles hardcoded handeln.

    Mathias schrieb:

    Wenn ich dadurch einen Programmabsturz vermeiden kann. Es soll Leute geben, die auch Code von Anderen über Schnittstellen ansprechen. Wenn da Müll geliefert wird muss mans irgendwie abfangen mindestens bis das nächste Patch des Fremdcodes draußen ist.

    Naja, nichtinitialiserte Zeiger sind immer noch logische Fehler. Über was du hier sprichst, sind letztendlich nichts anderes als zweifelhafte Hacks. Wenn jemand eine lausige Schnittstelle anbietet, dann würde ich ehrlich gesagt die entsprechende Funktonalität nicht nutzen. Ein zurückgelieferter Zeiger, der "evtl. nicht initialisiert sein könnte", ist für mich immer uninitialisiert. Punkt. Logisch gesehen gibt es nur Null und Zeiger auf ein gültiges Objekt. Alles andere ist nicht definiert und daher nicht zu gebrauchen. Normalerweise sollte ja aber auch eine Doku zur Schnittstelle vorhanden sein, in der alles wissenswerte steht.
    Ausserdem, funktioniert dynamic_cast überhaupt problemlos zwischen unterschiedlichen Modulen? Gibt es da einen einheitlichen Standard für RTTI? AFAIK schreibt der C++ Standard da nichts vor. Man kann ja nicht automatisch erwarten, dass jemand eine Bibliothek bereitstellt, die mit dem gleichen Compiler erstellt wurde, den man selbst verwendet.

    Aber wie ich bereits erwähnte und wie deine Aussagen auch dokumentieren

    Leider verwenden viele Leute den Cast zu leichtfertig und unbedacht.

    dynamic_cast sollte wirklich nur verwendet werden, wenn es keine andere Möglichkeit gibt. Vorher sollte man sich Gedanken über ein vernünftiges Design machen.



  • @groovemaster
    Ein gutes Design zeichnet sich nicht dadurch aus, dass man krampfhaft versucht um jeden Preis dynamic_cast aus dem Weg zugehen und dafür Sachen implementiert, die gar nicht gefragt sind.

    Eine allgemeine Schnittstelle zu definieren über die man alle möglichen und unmöglichen Attribute und Funktionen der abgeleiteten Klassen verwenden kann ist bei einer etwas komplexeren Aufgabe ein ziemlicher Schlag mich tot.
    🙄

    Logischer Fehler hin, zweifelhafte Hacks her... man gibt nicht hunderte oder taustende von € für Bibliotheken aus um sie dann nachher wegen ein paar Bugs nicht zu benutzen.
    Wenn du dir das leisten kannst, dann bitte... die meisten anderen werden versuchen diese trotzdem irgendwie zu nutzen.

    Bibliotheken kann man auch mit mehreren Compilern übersetzen. 😃

    Die Gedanken über ein ordentliches Design zu machen bevor man anfängt zu programmieren ist schön und gut, nur was machst du wenn du bereits geschriebenen Code erweitern sollst?
    (und das ist normalerweise die Regel)



  • Mathias schrieb:

    Ein gutes Design zeichnet sich nicht dadurch aus, dass man krampfhaft versucht um jeden Preis dynamic_cast aus dem Weg zugehen und dafür Sachen implementiert, die gar nicht gefragt sind.

    Das hat ja auch niemand behauptet. Nur, zu schnell greift man zu dynamic_cast, weil es einem angebracht erscheint. Aber sich Gedanken über ein gutes Konzept zu machen, fällt dann schnell unter den Tisch. Das ist dann die Feierabend Fraktion, die sich auch noch Programmierer nennen. Die bekommen Syntax und Semantik für ein Programm gerade noch irgendwie hin, aber Ahnung von Softwareentwicklung haben die nicht. Ich weiss aus eigener Erfahrung, dass bei vielen das Design leidet, ganz einfach, weil man praktisch immer unter Zeitdruck steht. Aber ein Grund, guten Stil für Bequemlichkeit einzutauschen, ist das trotzdem nicht. Ich sage ja nicht, dass du dein Design so wählen sollst, dass du kein dynamic_cast brauchst. Aber mit einem vernünftigen Design hast du erst gar nicht die Notwendigkeit, dynamic_cast benutzen zu müssen. Und wenn, dann wirklich nur extrem selten.

    Mathias schrieb:

    Eine allgemeine Schnittstelle zu definieren über die man alle möglichen und unmöglichen Attribute und Funktionen der abgeleiteten Klassen verwenden kann ist bei einer etwas komplexeren Aufgabe ein ziemlicher Schlag mich tot.

    Nee, du sollst ja nicht alles an die Schnittstelle ziehen, sondern nur das, was man brauchst. Eben das, wofür du sonst dynamic_cast verwenden müsstest.

    Mathias schrieb:

    Logischer Fehler hin, zweifelhafte Hacks her... man gibt nicht hunderte oder taustende von € für Bibliotheken aus um sie dann nachher wegen ein paar Bugs nicht zu benutzen.

    Tatsächlich? Du hast relativ wenig Ahnung von der Materie, stimmts? Wieso erzählst du das nicht mal Leuten, die Software fürs Finanzwesen oder Automatisierungstechnik schreiben? Wir reden hier nicht über irgendwelche unbedeutenden Bugs, die vielleicht mal einen Button auf irgendeiner Oberfläche eines Hobbyprogrammierers nicht zur Anzeige bringen. Wir reden hier ua auch von hochsensiblen Bereichen, wo jeder Bug fatal sein kann und damit einer zu viel ist. Und wenn der Entwickler der Bibliothek zu blöd ist, den Zeiger korrekt zu initialisieren, ob nun mit einem Objekt oder einem Nullzeiger, dann ist er schlichtweg fehl am Platz. Da kannst du dich drehen und wenden wie du willst, dynamic_cast ist und bleibt an dieser Stelle ein Hack und ist keine saubere Lösung. Für mich wäre deshalb die Bibliothek an dieser Stelle einfach unbrauchbar (auch wenn man idR das ganze wohl trotzdem irgendwie zum Laufen bringen muss 😉 ).

    Mathias schrieb:

    Bibliotheken kann man auch mit mehreren Compilern übersetzen. 😃

    Ja, super. Wenn du einen so netten Partner hast, der dir den Quellcode gibt oder die Bibliothek in allen möglichen Formaten zur Verfügung stellt. 🙄

    Mathias schrieb:

    Die Gedanken über ein ordentliches Design zu machen bevor man anfängt zu programmieren ist schön und gut, nur was machst du wenn du bereits geschriebenen Code erweitern sollst?
    (und das ist normalerweise die Regel)

    Naja, die Regel ist es mit Sicherheit nicht. Aber das ist ja nur ein weiterer Grund, seiner Anwendung ein sauberes Design zu verpassen, damit die Leute, die es warten oder erweitern müssen, nicht von einer Baustelle zur nächsten gelangen und dabei die eigentliche Aufgabe aus den Augen verlieren.



  • Tatsächlich? Du hast relativ wenig Ahnung von der Materie, stimmts?

    Bevor man jemanden unterstellt das er keine Ahnung hat sollte man wissen mit wem man redet.
    🙄

    Ja, super. Wenn du einen so netten Partner hast, der dir den Quellcode gibt oder die Bibliothek in allen möglichen Formaten zur Verfügung stellt.

    Genau das machen die meisten Leuts, die Bibliotheken verkaufen/bereitstellen wollen. 😃
    (Find ich zwar ehrlich gesagt auch ziemlich verrückt, aber so ist das reale Leben.)

    Alle anderen Kommentare spar ich mir jetzt mal, ich hab ja sowieso keine Ahnung.


Anmelden zum Antworten