Warum programmieren einige noch in C?



  • In C kann man doch sehr wohl objekt orientiert arbeiten, eine Struct mit Funktionen, die diese bearbeiten, ist doch bereits ein Objekt. Wenn eine neue Struct die alte mit drin hat und erweitert, dann ist das eine Vererbung.
    @Jockelx sagte in Warum programmieren einige noch in C?:

    @Computerwelt sagte in Warum programmieren einige noch in C?:

    @Bashar sagte in Warum programmieren einige noch in C?:

    Daraus wurde aber irgendwann eine Ideologie, nämlich in dem Moment, an dem "das ist aber nicht objektorientiert" für sich genommen schon zu einer validen Kritik wurde. Es wurde nicht mehr nachgedacht, sondern jedem Anfänger wurde eingebleut, irgendwelche "Klassen" Person mit getAge und setName usw. zu schreiben.

    Genau so etwas finde ich schrecklich.

    Und was genau stört dich? Auch in C würde ich erstmal erwarten, dass Person(en) in einer struct dargestellt werden.
    Getter/setter kann man ja gerne ablehnen, aber ansonsten würden sich das C und C++ Design einer Person wohl kaum unterscheiden (ausser natürlich das ich in C z.B. keine string-Klasse habe und mich mit ekeligen char-arrays rum ärgern muss)

    Mich stört es dass bei jedem Furz gleich Getter Setter genommen werden. Durch eine Struct eine Zugehörigkeit zu pflegen, finde ich hingegen gut. Schön wären in C Namensräume, warum gibt es die nicht?



  • @Computerwelt sagte in Warum programmieren einige noch in C?:

    Wenn eine neue Struct die alte mit drin hat und erweitert, dann ist das eine Vererbung.

    Gefährliche Aussage, da die C Vererbung nur dann funktioniert wenn die vererbte Klasse an der ersten Stelle im Struct steht. Ansonsten fängt man sich einen bitterbösen Fehler ein. Ein Beispiel:

    #include <stdio.h>
    
    typedef struct CartesianCoord 
    {
        double x;
        double y;
        double z;
    };
    
    typedef struct PointWrong
    {
        char Name[256];       
        CartesianCoord Base;    
        unsigned long ID;
    };
    
    typedef struct PointCorrect
    {
        // Hier nichts einfügen, da wir uns sonst einen gewaltigen Fehler einfangen.
        CartesianCoord Base;    
        char Name[256];       
        unsigned long ID;
    };
    
    void Init_CartesianCoord(CartesianCoord* c)
    {
        c->x = 1;  // nur ein Beispiel
        c->y = 2;
        c->z = 3;
    }
    
    void Init_PointWrong(PointWrong* p)
    {
        p->Name[0] = 0;
        Init_CartesianCoord((CartesianCoord*) p);    
        p->ID = 1;
    }
    
    void Init_PointCorrect(PointCorrect* p)
    {
        p->Name[0] = 0;
        Init_CartesianCoord((CartesianCoord*) p);    
        p->ID = 1;
    }
    
    int main(int argc, char **argv)
    {
        PointWrong p;
        PointCorrect p2;
        
        Init_PointWrong(&p);
        printf("x=%f\ty=%f\tz=%f\tName=%s\tID=%lu\n", p.Base.x, p.Base.y, p.Base.z, p.Name, p.ID);
            
        Init_PointCorrect(&p2);
        printf("x=%f\ty=%f\tz=%f\tName=%s\tID=%lu\n", p2.Base.x, p2.Base.y, p2.Base.z, p2.Name, p2.ID);
        return 0;	
    }
    

    Als Ausgabe bekomme ich:

    x=0.000000      y=0.000000      z=0.000000      Name=   ID=1
    x=1.000000      y=2.000000      z=3.000000      Name=   ID=1
    

    Natürlich könnte man auch die Init Funktion folgendermaßen aufrufen:

    Init_CartesianCoord(&p->Base);
    

    Aber ich glaube einige Leute würden das dann als Aggregation ansehen und die nächste Diskussionrunde würde losbrechen.



  • @Computerwelt sagte in Warum programmieren einige noch in C?:

    Mich stört es dass bei jedem Furz gleich Getter Setter genommen werden.

    Auch Setter und Getter sollte man "bewusst" einsetzen. Ich mag das auch nicht, wenn ganzen Klassen mit denen vollgeschissen sind. Meist gibt es Alternativen, die genauso sicher sind und trotzdem elegant sind.

    Ich befülle Klassen auch gerne mal über sowas:

    void loadFromFile(...);
    void loadFromOtherData( const OtherData & );
    void parseString( const std::string & );
    void loadFromXML( const XMLNode & );
    

    Je nach Anwendungsfall findet man da durchaus Möglichkeiten, um sowas wie das hier zu vermeiden:

    MyObject.setMemberA(... );
    MyObject.setMemberB(... );
    MyObject.setMemberC(... );
    MyObject.setMemberD(... );
    MyObject.setMemberE(... );
    

    C++ bietet mehr als genug Möglichkeiten, um Dinge auch elegant zu lösen. Man muss nur drauf kommen 😉

    Was im allgemeinen aber keine Lösung ist: Alles public machen.
    Funktionieren wird das schon, aber gerade bei größeren Projekten, mit mehreren Entwicklern, entsteht da schnell Schlamperei, weil dann kreuz und quer die Member verändert werden.



  • @It0101

    Was im allgemeinen aber keine Lösung ist: Alles public machen.

    Im allgemeinen: ja. Aber das wird halt auch gerne falsch verstanden bzw. übertrieben. Was dann eben zu diesen lustigen "getter und setter für alles" Klassen führt*. Sowas sollte man dann lieber gleich als nackte struct machen.

    Und es gibt schon Fälle wo nackte structs Sinn machen. z.B. wenn ich Daten erstmal unvalidiert aus einem File oder auch von einem Netzwer-Stream lesen will, dann macht es keinen Sinn class zu schreiben und für jedes Feld einen Getter und Setter anzubieten.

    Bei sowas will ich direkt beim Deserialisieren eher keine Validierung die über die Regeln des Serialisierungsprotokolls hinausgeht. Das will ich dann nachher machen. Einmal um unterscheiden zu können ob der Datenstrom einfach komplett hin ist oder ob inkonsistende Daten korrekt serialisiert wurden. Und dann ist es auch manchmal hilfreich wenn man das komplette Datenpaket bei einem Validierungsfehler schon deserialisert hat und es z.B. in lesbarer Form in ein Logfile schreiben kann.

    Oder, ganz extremes Beispiel: Eine Vector3D Klasse muss nun wirklich keine getX/setX, getY/setY, getZ/setZ Funktionen haben. Das darf ruhig (bzw. sollte sogar IMO) einfach public sein.

    *: Das ist natürlich nur ein Weg wie es zu den "getter und setter für alles" Klassen kommt. Natürlich gibt es auch Leute die das ganze überhaupt nicht verstanden haben und auch "getter und setter für alles" Klassen in Fällen machen wollen wo Kapselung sinnvoll ist und es sehrwohl Invarianten aufrecht zu erhalten gibt.



  • @hustbaer sagte in Warum programmieren einige noch in C?:

    Und es gibt schon Fälle wo nackte structs Sinn machen

    Es ist auch sehr gut zu lesen, wenn man sich an die Konvention hält, dass in structs alles public ist.
    Dann drückt das eine Menge aus, wenn jemand ein struct verwendet.



  • @hustbaer
    Das was du beschreibst sind auch ungefähr die Ausnahmefälle, die mir so in den Sinn kamen. Wenn ich z.B. ein Message-Format habe, welches immer 10 Byte Header hat, dann ist so ein struct in das ich einfach 10 Byte reinkopiere aus meiner Sicht die bessere Wahl, im Vergleich zum einzelnen parsen und setzen der Werte in einer Klasse. ( unter Beachtung des data structure alignments natürlich 😄 ).



  • Gibt es denn eigentlich Bücher in denen schönes C++ gelehrt wird? Die meiste Fachliteratur schreibt nur alle Möglichkeiten runter, aber was davon best practice ist und was man lieber lässt, erfährt man nicht.



  • @Computerwelt
    Ich habe die meisten der Bücher nicht gelesen, aber es gibt hier: https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list/388282#388282
    und hier https://www.c-plusplus.net/forum/topic/251551/bücher-c-lernen-passende-lektüre-und-richtiger-anfang/3 eine Liste mit Büchern.
    Ich selbst habe "Effective modern C++" von Scott Meyers hier rum liegen, würde das aber nicht als typisches Lehrbuch empfehlen, sondern eher als weiterführende Literatur.



  • Ist kein Buch, aber trotzdem nützlich: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines



  • Das hier ist auch gut:
    https://herbsutter.com/gotw/



  • Danke für die Tipps



  • Wo wir gerade bei Settern und Büchern sind... ich empfehle an der Stelle den Stroustrup ("The C++ Programming Language"). Wird gerne als Referenz missverstanden, hat aber durchaus einiges zu sagen, in einem sehr unaufgeregten Stil, wenig Predigten, viele Ratschläge und jeder Winkel von C++ wird beleuchtet. Warum ich den in dem Zusammenhang empfehle: Stroustrup legt großen Wert darauf, dass der Sinn der Kapselung darin besteht, eine Invariante zu schützen. Ich will nicht exakt erklären, was das ist, aber ein Beispiel nennen: Eine Klasse für Brüche besteht in der Regel aus zwei Ganzzahlen, von denen eine, der Nenner, immer ungleich Null sein muss. Es ist der Job des Konstruktors, dafür zu sorgen, dass das von Anfang an so ist, und es ist der Job jeder Memberfunktion (und jedes Friends), dafür zu sorgen, dass das so bleibt. Dafür muss man sich nie fragen, was eigentlich passieren soll, wenn es doch mal nicht stimmt, weil das nie vorkommen kann. Das Interface der Klasse muss so entworfen sein, dass es von außen nicht möglich ist, die Invariante zu zerstören. Oder aus der anderen Perspektive: Es ist nicht der Job des Restes der Welt, die Invariante zu bewahren, sondern das ist Aufgabe der Klasse. Übrigens folgt aus dieser Betrachtung auch, dass Setter kritisch sind -- entweder können sie die Invariante zerstören, oder sie können fehlschlagen (wenn man versucht etwas ungültiges zu setzen und das abgefangen wird), oder sie betreffen einen Teil der Daten, der an der Invariante nicht beteiligt ist, was evtl. auf eine Verletzung des Single-Responsibility-Principles hinweist.



  • @Computerwelt sagte in Warum programmieren einige noch in C?:

    Gibt es denn eigentlich Bücher in denen schönes C++ gelehrt wird? Die meiste Fachliteratur schreibt nur alle Möglichkeiten runter, aber was davon best practice ist und was man lieber lässt, erfährt man nicht.

    Es gibt regalmeterweise Bücher die sich mit diesem Themenkomplex befassen. Das Problem ist nur, dass viele dieser Bücher vor C++11 entstanden sind, und es nicht von allen Neuauflagen mit angepassten Inhalt gibt. Aktuelle Bücher sind dann meist für C++11 und/oder für C++14 geschrieben.

    • Effective Modern C++; Scott Meyers (es gibt noch die drei alten Bücher auf dem Stand von C++98)
    • Exceptional C++, More Exceptional C++, Exceptional C++ Style; Herb Sutter (leider auf dem Stand von C++98)
    • Programming: Principles and Practice Using C++; Bjarne Stroustrup

Anmelden zum Antworten