john 0 erklaert C++ casts



  • @john-0 sagte in john 0 erklaert C++ casts:

    @Bashar sagte in john 0 erklaert C++ casts:

    Wenn reinterpret_cast benutzt wird, um einen Zeiger auf T in einen Zeiger auf U zu konvertieren, ist das per Definition dasselbe wie den Zeiger auf T zuerst mithilfe von static_cast in einen Zeiger auf void, und das Ergebnis dann mit static_cast in einen Zeiger auf U zu konvertieren.

    Das Problem bei reinterpret_cast ist, dass man da mit nicht nur die Zeiger konvertieren kann, sondern

    #include <cstdlib>
    
    struct Foo{};
    
    int main () {
        size_t s = 1;
        Foo* pF = reinterpret_cast<Foo*>(s);  // 1
        Foo* p  = reinterpret_cast<Foo*>(&s); // 2
    }
    

    auch das hier. Wenn Du den Unterschied zwischen Zeile 1 und 2 verstanden hast, dann können wir weiter diskutieren.

    das geht aber doch mit dem C-Style-Cast genauso ( Zeile 1 ist gemeint ) ....



  • @It0101 sagte in john 0 erklaert C++ casts:

    das geht aber doch mit dem C-Style-Cast genauso ( Zeile 1 ist gemeint ) ....

    Ja, und deshalb sollte man weder C-Style-Casts noch reinterpret_cats verwenden, wenn dies nicht unbedingt notwendig ist. Du musst nur ein "&" oder "*" vergessen und schon hast Du UB ins Programm eingebaut und der Compiler wirft keinerlei Warnung aus.



  • @john-0 sagte in john 0 erklaert C++ casts:

    Das geht problemlos via

    struct T{};
    
    char*p;
    void* pv = static_cast<void*>(p);
    T* pT = static_cast<T*>(pv);
    

    Das kompiliert zwar, aber es ist trotzdem fürchterlich.



  • @john-0 sagte in john 0 erklaert C++ casts:

    @It0101 sagte in john 0 erklaert C++ casts:

    das geht aber doch mit dem C-Style-Cast genauso ( Zeile 1 ist gemeint ) ....

    Ja, und deshalb sollte man weder C-Style-Casts noch reinterpret_cats verwenden, wenn dies nicht unbedingt notwendig ist. Du musst nur ein "&" oder "*" vergessen und schon hast Du UB ins Programm eingebaut und der Compiler wirft keinerlei Warnung aus.

    Da stimme ich dir zu. C-Style-Casts und auch reinterpret sollten absolute Ausnahmen sein. Ich brauche die Schlampen-Casts ( so fasse ich die zusammen ) eigentlich fast nie. Eigentlich caste ich überhaupt relativ selten. Ist auch heutzutage selten notwendig, wenn man sauber mit Templates und Vererbung arbeitet.

    Ich benutze diese Casts eigentlich nur hardware-nah bei der Speicherbesorgung und eben bei Puffern, wenn man z.B. den Typ kennt, der als nächstes im Puffer folgt, dann caste ich auch mal von char* nach T*.



  • @It0101 sagte in john 0 erklaert C++ casts:

    Das kompiliert zwar, aber es ist trotzdem fürchterlich.

    Es ist eine Folge des gewählten Designs.



  • Ich habe eigentlich nicht verstanden warum ihr euch hier so in die Haare bekommt.
    Dass C-Style und reinterpret_cast zu vermeiden sind, ist doch eigentlich Konsens.



  • C-Style Casts sind viel übersichtlicher und nicht in langnamigen Funktionen versteckt. Ausnahmen für mich sind hier std::move() weil der Name vergleichsweise besser aussieht.

    So schlimm kann das schon nicht sein das richtig zu machen.



  • @eigenartig sagte in john 0 erklaert C++ casts:

    C-Style Casts sind viel übersichtlicher und nicht in langnamigen Funktionen versteckt

    Hast du kein Intellisense / Autovervollständigen?
    Für die meisten Befehle muss ich gerade mal vier Buchstaben eingeben.

    Also das schlampige Arbeiten mit "ich will nicht soviel tippen" zu begründen, ist in der heutigen Zeit schon bedenklich 😉 Nichts für ungut.



  • @It0101 sagte in john 0 erklaert C++ casts:

    Also das schlampige Arbeiten mit "ich will nicht soviel tippen" zu begründen, ist in der heutigen Zeit schon bedenklich Nichts für ungut.

    Das stimmt doch gar nicht. Es ist einfach viel hübscher nur (T) zu verwenden.



  • @It0101 sagte in john 0 erklaert C++ casts:

    Ich habe eigentlich nicht verstanden warum ihr euch hier so in die Haare bekommt.

    Der Herr reagiert sehr dünnhäutig darauf, wenn man seinen Stil zu programmieren kritisiert. Einige hier im Forum hängen einer Ideologie an, dass man keinerlei manuelle Speicherverwaltung machen dürfe. Das führt dann in diesem konkreten Beispiel zu einem zweifelhaften Design. Wann wenn nicht in einem Container sollte man manuelle Speicherverwaltung machen?

    P.S. Wie man es ohne cast hinbekommt, habe ich im original Thread mittlerweile gepostet. Das ist die Minimallösung. Die saubere Lösung wäre die Containerklasse komplett mit eigener Speicherverwaltung zu schreiben, und dann wird auch schnell ersichtlich, weshalb propagate_on_container_swap, propagate_on_container_copy_assigment, propagate_on_container_move_assigment alles andere als Trivialitäten sind.



  • @eigenartig sagte in john 0 erklaert C++ casts:

    C-Style Casts sind viel übersichtlicher

    Sie verdecken Fehler, und das ist ein Problem. Dazu kann man dynamic_cast nicht durch einen C-Cast ersetzen.



  • @john-0 sagte in john 0 erklaert C++ casts:

    Sie verdecken Fehler, und das ist ein Problem.

    C++-Style Casts können auch falsch benutzt werden. Darüber streitet ihr euch doch.

    @john-0 sagte in john 0 erklaert C++ casts:

    Dazu kann man dynamic_cast nicht durch einen C-Cast ersetzen.

    Eine Klasse mittels dynamic_cast<> herauszufinden ist sowieso fragwürdiges Design.



  • @eigenartig sagte in john 0 erklaert C++ casts:

    C++-Style Casts können auch falsch benutzt werden.

    Ja, aber da schrillen bei einem const_cast<> oder einem reinterpret_cast<> sofort die Alarmglocken.

    @eigenartig sagte in john 0 erklaert C++ casts:

    Eine Klasse mittels dynamic_cast<> herauszufinden ist sowieso fragwürdiges Design.

    weil?



  • @john-0 sagte in john 0 erklaert C++ casts:

    Der Herr reagiert sehr dünnhäutig darauf, wenn man seinen Stil zu programmieren kritisiert. Einige hier im Forum hängen einer Ideologie an, dass man keinerlei manuelle Speicherverwaltung machen dürfe. Das führt dann in diesem konkreten Beispiel zu einem zweifelhaften Design. Wann wenn nicht in einem Container sollte man manuelle Speicherverwaltung machen?

    Das sehe ich eigentlich ähnlich wie "einige". Es gibt kaum noch plausible Gründe selber Speicher zu allokieren.



  • @eigenartig sagte in john 0 erklaert C++ casts:

    Eine Klasse mittels dynamic_cast<> herauszufinden ist sowieso fragwürdiges Design.

    Nur mal ein Beispiel warum C-Style-Casts nicht so toll sind:

    class A {};
    class B : public A {};
    class C {};
    
    int main( int argc, char **argv)
    {
        B b;
        // B Kommt als Basisklasse daher, wie es oft ist
        A *a = &b;
    
        // C-Style-Cast
        C *c1 = (C*)a; // Das kompiliert
    
        // C++-Style-Cast
        C *c2 = dynamic_cast<C*> ( a ); // Das kompiliert nicht
    

    D.h. schon der Compiler weist dich bei der C++-Cast-Variante darauf hin, dass hier was nicht stimmt.
    Übrigens mit der Meldung

    "error: cannot dynamic_cast 'a' (of type 'class A*') to type 'class C*' (source type is not polymorphic)|"

    Bei der C-Style-Cast-Variante läufst du ins offene Messer, rufst irgendwann eine Funktion deines Objects "c1" auf und dann machts bumm. Vielleicht nicht sofort. Wenn es eine selten genutzte Funktion ist, in der du castest knallt es vielleicht erst in 10 Wochen, während der verantwortliche Entwickler im Urlaub ist und der ahnungslose Kollege gerade das Hanfseil am Balkon befestigt, weil er die Crash-Ursache in Produktion nicht findet und die Kunden beim Support Sturmklingeln.

    Dann kommt "eigenartig" aus dem Urlaub zurück und sagt: "ja ich fand C-style-Casts viel hübscher. That's my style bitches!".



  • @It0101 sagte in john 0 erklaert C++ casts:

    Das sehe ich eigentlich ähnlich wie "einige". Es gibt kaum noch plausible Gründe selber Speicher zu allokieren.

    „Kaum noch“ heißt aber nicht „keine“ und insbesondere Container (alle Standardcontainer haben optionale Argumente für Allokatoren) sind hier die Ausnahme. Wenn man einen Standardcontainer nachbauen will, bzw. einen Container im Stile der Standardcontainer realisieren will, wird man um die Verwendung von Allokatoren nicht herum kommen. Wenn man Allokatoren nutzt, ist auch das Cast-Problem aus der Welt.



  • @john-0 Warum sollte man einen Standard-Container nachbauen wollen? Gerade die Container sind im Gegensatz zu den Strings oder StreamStreams sogar sehr performant und konnten zumindest meine Bedürfnisse bis jetzt immer erfüllen.



  • @It0101 sagte in john 0 erklaert C++ casts:

    @john-0 Warum sollte man einen Standard-Container nachbauen wollen?

    Genau das war aber das Thema des Threads aus dem mein Posting ausgelagert wurde.

    Ist es sinnvoll? Nein, da wirst Du von mir keinen Widerspruch hören. Es ist sinnvoll einen Container einmal selbst implementiert zu haben – möglicherweise.



  • @john-0 sagte in john 0 erklaert C++ casts:

    @It0101 sagte in john 0 erklaert C++ casts:

    @john-0 Warum sollte man einen Standard-Container nachbauen wollen?

    Genau das war aber das Thema des Threads aus dem mein Posting ausgelagert wurde.

    Ist es sinnvoll? Nein, da wirst Du von mir keinen Widerspruch hören. Es ist sinnvoll einen Container einmal selbst implementiert zu haben – möglicherweise.

    Klar macht das Sinn, wenigstens einmal eine Container-Struktur gebaut zu haben. Nicht unbedingt weil man viel über Iteratoren lernt, sondern weil man was über das saubere Arbeiten mit Speicher, Pointern, Speicherblöcken usw. lernt.

    Das Wissen kann man allerdings auch anderswo aufsammeln. Z.B. wenn man mit Sockets arbeitet und Protokolle entwirft. Aber ich stimme dir zu: Es schadet nicht, es mal gemacht zu haben.



  • @It0101 sagte in john 0 erklaert C++ casts:

    Warum sollte man einen Standard-Container nachbauen wollen?

    Vielleicht, weil man bestimmte Eigenschaften ausnutzen will - z.B. wenn man nur über die Daten weiß, dass man sie relocaten kann und die Werte einfach im Speicher rumgeschoben werden können, ohne Konstruktoren aufrufen zu müssen? So ein Container für PODs ohne interne Pointer zum Beispiel. Da kann man beim resizen z.B. realloc verwenden, wenn man will. Oder auch memcopy anstatt Konstruktoren und Destruktoren aufzurufen. std::vector ist voll generisch und muss all diese Spezialfälle abdecken. Wenn man darauf verzichten kann, kann man u.U. besser sein.


Anmelden zum Antworten