Warum programmieren einige noch in C?



  • Also kann man in C++ auch schön programmieren ohne den Zwang zu unterliegen aus allem gleich ein Objekt machen zu müssen?



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

    Also kann man in C++ auch schön programmieren ohne den Zwang zu unterliegen aus allem gleich ein Objekt machen zu müssen?

    Du kannst ja das C von C++ benutzen 😄



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

    Wenn sie echt objektorientiert wäre -- also von jemandem entworfen, der von OOP schonmal was gehört hat --,

    Du vermischt gerade Intention und Ergebnis.

    OOP ist eine spezifische Entwurfs/Programmiermethode. Man kann etwas OOP nennen, wenn es dieser Methode entspricht. Das kann theoretisch zufällig passieren, aber dann muss es eben so aussehen als ob der Autor diese Methode befolgt hat.

    BTW: Anscheinend gibt es den Begriff "object oriented" schon seit den späten 50er/frühen 60er Jahren. (Weil du vorhin schriebst (sinngemäss) es wäre irre anzunehmen etwas aus den 70ern könne objektorientiert sein...)

    Und was ist damit gemeint? Es ist ja nicht sehr fernliegend, sich irgendwie an Objekten zu orientieren.

    könnte man z.B. eigene Streamtypen definieren und mit der API verwenden.

    Ähm. Das könnte man dann vielleicht, aber nicht notwendigerweise. Und Gründe etwas nicht polyporph bzw. nicht erweiterbar polymorph zu machen gibt es mMn. viele. KISS, Performance, Implementierungsoverhead und dass es verdammt schwer ist nicht-triviale APIs sauber erweiterbar polymorph zu machen.

    Für meine Begriffe sagst du damit, es gab gute Gründe, diese API nicht objektorientiert zu machen. Die Gründe lagen praktisch darin, dass C das nicht so einfach ermöglicht und dass die Entwickler das auch gar nicht im Sinn hatten, das zwar am Rande, aber es sollte auch nicht in Vergessenheit geraten.

    Ich wollte darauf hinaus dass die Aussage "wer die (irre) Behauptungen aufstellt, muss Beweise führen" auch genau so für dich gilt, da du ebenso (irre) Behauptungen aufstellt hast.

    Aus meiner Sicht nicht. Ich stehe auf dem Standpunkt, das ich lediglich die Mainstream-Sichtweise, was OOP ist, wiedergebe. Und ihr euch aus nicht nachvollziehbaren Gründen für eine Definition entschieden habt, die so umfassend ist, dass sie aus meiner Sicht schon fast bedeutungslos ist. Ich muss mich nicht dafür rechtfertigen, dass ich genausogut Wikipedia oder irgendein relevantes Buch aus dem Regal nehmen und die Einführung zitieren könnte. Ihr müsst das, wenn ihr Entwürfe, die vor der OOP-Ära entstanden sind, als "mustergültige OOP" bezeichnet.

    Niemand hat behauptet dass C objektorientierte Programmierung besonders einfach macht

    Ähm, doch? Hast du den Thread überhaupt gelesen?

    Ja, doch. Muss mir aber entgangen sein. Wo? Wer?

    Wutz hat behauptet, OOP in C sei "sehr gut" machbar.

    Ich habe irgendwie den Eindruck dass du siehst OOP als eine Art Religion oder noch schlimmer: Kirche. Für mich ist das einfach nur ein Konzept bzw. eine lose Ansammlung von Konzepten, wovon manche wichtiger sind und manche weniger wichtig.

    Naja, da muss man schon trennen. OOP ist erstmal eine Idee. Für mich ist die Grundidee die von Alan Kay, aber auch nur weil ich Simula nicht kenne. Dann ist es die Geschichte dieser Idee ... C++, Objective-C, Java, UML, Design Patterns, SOLID etc.

    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. Man hat erstmal irgendwelche Begriffshierarchien analysiert und dann mit Vererbungshierarchien nachgebildet. Es gab ein Heilsversprechen, und das ist der Punkt an dem man es vielleicht auch Religion nennen kann: Halte dich an diese Vorgehensweise, auch wenn es sinnlos erscheint, die Belohnung zeigt sich erst sehr viel später in richtig großen Projekten. Es gab die Idee, dass im Prinzip alle objektorientierten Programmiersprachen gleich sind, dass man einen objektorientierten Entwurf abstrakt am Whiteboard (oder UML-Tool) durchführen und dann mechanisch in Code umsetzen kann.

    Die Ideen haben sich in der Zwischenzeit natürlich gewandelt, das ist schon seit einiger Zeit nicht mehr so, insbesondere Vererbungshierarchien sind in Ungnade gefallen, seit man angefangen hat, mehr über Kopplung und Kohäsion nachzudenken.

    So eine Ideologie ist aber auch unausweichlich. Jedes Paradigma macht manche Dinge leichter und manche schwerer als andere Paradigmen, so dass es eine Rechtfertigung erfordert, sich dem zu unterwerfen. Der eine mag seine dynamisch typisierte Sprache und nimmt Typfehler zur Laufzeit in Kauf, für den anderen ist das der Horror und er bevorzugt Sprachen mit starkem Typsystem. Ich hab trotzdem das Gefühl, dass das bei der OOP nochmal eine gesteigerte Dynamik angenommen hat.

    Ich lege diese "Kirche" aber nicht als Definition für OOP zugrunde, wie du behauptest. Was bleibt ist, dass OOP eine spezifische Idee ist. In C++ gehört dazu z.B. die Verwendung von Laufzeitpolymorphie. Oft kann man ja ein bestimmtes Problem auf verschiedene Arten lösen. Wenn ich das mithilfe von OOP löse, werde ich vermutlich einige Klassen schreiben, ich werde virtuelle Funktionen anwenden. Ich kann es aber auch anders lösen. OOP ist somit ein Werkzeug, das ich anwende, wenn es passt.

    Es kann auch sein, dass das Problem so simpel ist, dass sich spezifische OOP-Techniken einfach nicht anbietet. Dann halte ich es für überkandidelt, das OOP zu nennen.

    Wenn du es nicht objektorientiert nennen willst... wie nennst du dann das was heute in sinnvoll und gut entworfenen C, C++ oder C# Programmen gemacht wird wo mit sauber gekapselten Objekten gearbeitet wird, erweiterbare oder geschlossene Polymorphie dort und nur dort eingesetzt wird wo sie Sinn macht etc.?

    Willst du sagen, dass C, C++ und C#-Programme heute mit derselben Methodik entworfen werden? Warum sollte es dafür einen gemeinsamen Namen geben?

    Was macht es für einen Sinn, jeglichen guten, modernen, übersichtlichen Programmentwurf objektorientiert zu nennen?

    Vielleicht noch ein abschließender Gedanke. Ich denke, dass wir im Wesentlichen übereinstimmen. Ihr behauptet nicht, dass in C OOP (wie ich es verstehe) "sehr gut" machbar ist, oder dass die C-Standardlibrary ein "Musterbeispiel" von OOP (wie ich es verstehe) ist. Ihr habt genau die gleichen Bauchschmerzen mit OOP (wie ich es verstehe) wie ich. Der Unterschied ist, dass ihr diese Schmerzen vermeidet, indem ihr die Definition von OOP abschwächt, während ich sage: Das ist dann eben nicht OOP, so what?



  • @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.



  • @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)



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

    Vielleicht noch ein abschließender Gedanke. Ich denke, dass wir im Wesentlichen übereinstimmen. Ihr behauptet nicht, dass in C OOP (wie ich es verstehe) "sehr gut" machbar ist, oder dass die C-Standardlibrary ein "Musterbeispiel" von OOP (wie ich es verstehe) ist. Ihr habt genau die gleichen Bauchschmerzen mit OOP (wie ich es verstehe) wie ich.

    Das wird wohl stimmen, ja.

    Der Unterschied ist, dass ihr diese Schmerzen vermeidet, indem ihr die Definition von OOP abschwächt, während ich sage: Das ist dann eben nicht OOP, so what?

    Diese Formulierung impliziert/unterstellt dass wir wüssten was die "eigentliche" Definition von OOP wäre, und diese dann abschwächen. Das ist nicht so. Objektorientiert bzw. objektorientierte Programmierung heisst für mich primär dass man Code und Daten in "Objekte" zusammenfasst. Alles weitere kann man machen, muss man aber nicht. Das ist meine primäre Definition, nicht eine abgeschwächte um irgend welche Probleme zu vermeiden.

    Und was das Problem dabei ist das nicht OOP zu nennen? Ganz einfach: du schreibst ein Programm das Objekte verwendest, und sagst dass es nicht objektorientiert ist. Das macht doch keinen Sinn.



  • 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