Objektorientiert Programmieren in C



  • hustbaer schrieb:

    Nein, C ist nicht cool. Es ist ne ganz schlimme Seuche. U.a. deswegen weil es in C unvernünftig viel Aufwand ist bestimmte Dinge sauber zu machen, die eigentlich einfach sein sollten.

    C ist cool.
    C ist in der Hand von Könnern unschlagbar (siehe Torvalds).
    C ist keine schlimme Seuche. C ist in der Hand von Deppen eine Seuche.
    Nur ist es in C einfacher, Unsinn zu programmieren als z.B. in C++, wo einem ja bekanntlich "bestimmte Dinge" abgenommen werden. Und deswegen gehört C auch nicht in die Hände von Anfängern oder Professoren oder Buchautoren oder Theoretikern (die glauben, C "verbessern" zu müssen, damit auch der letzte Depp glaubt, er könne programmieren weil er unfallfrei ein paar Zeilen aneinanderreihen kann).
    Für wiederkehrende Anforderungen im Tagesgeschäft gibt es zuhauf Bibliotheken.
    Wenn sich nämlich Könner - die C durch und durch verstanden haben - die Aufgabe stellen, C an die Neuzeit anzupassen, dann kommt was anderes raus als C++:
    http://www.drdobbs.com/open-source/interview-with-ken-thompson/229502480



  • Wutz schrieb:

    Wenn sich nämlich Könner - die C durch und durch verstanden haben - die Aufgabe stellen, C an die Neuzeit anzupassen, dann kommt was anderes raus als C++

    Das nennt sich dann C90, C95, C99, C11 und Objective-C.



  • Haltet ihr das offizielle Werk "The C Programming Language" (Zweite Auflage) vom Entwickler der Sprache für eine gute Einstiegslektüre? Ich verfüge bereits über geringe Programmiererfahrungen, stehe also nicht auf dem Schlauch wenn er beispielsweise das Wort "Kontrollstrukturen" benutzt.



  • Hi nochmal. Ich habe zwar meine Präferenzen, aber dennoch nicht vor, mich an dem hier aufziehenden Flamewar zu beteiligen. Meine Einstellung zu C ist neutral,
    und mir ist bewusst dass vieles von meiner Arbeit mit C++ auf Problemlösungen basiert, die auf elegante Weise in C implementiert wurde.

    Ich möchte hier lediglich ein Beispiel dafür geben, weshalb mir die "manuellen Destruktoren" so wenig zusagen.
    Der folgende Code stammt aus dem GStreamer-Projekt, mit dem ich vor einiger Zeit zu tun hatte, da wir dafür für unser Projekt ein kleines Spezial-Plugin
    benötigten. Ich glaube die Codepassage hier ist ein gutes Beispiel dafür, wie objektorientierter C-Code in einem größeren Projekt aussieht. Es handelt
    sich hierbei nicht um ein Extrembeispiel, weder in die eine oder die andere Richtung. Ich habe die Funktion deshalb ausgesucht, weil sie meiner Meinung den
    durchschnittlichen Fall recht gut repräsentiert. Die Kommentare stammen von mir:

    static GstPad *
    gst_decode_group_control_demuxer_pad (GstDecodeGroup * group, GstPad * pad)
    {
      GstDecodeBin *dbin;
      GstPad *srcpad, *sinkpad;
      GstIterator *it = NULL;
      GValue item = { 0, };
    
      dbin = group->dbin;
    
      GST_LOG_OBJECT (dbin, "group:%p pad %s:%s", group, GST_DEBUG_PAD_NAME (pad));
    
      // Hilfsvariable indirekt notwendig weil Kontrollfluss vor "return" durch Aufräumcode laufen muss.
      srcpad = NULL; 
    
      if (G_UNLIKELY (!group->multiqueue))
        return NULL;
    
      if (!(sinkpad = gst_element_get_request_pad (group->multiqueue, "sink_%u"))) {
        GST_ERROR_OBJECT (dbin, "Couldn't get sinkpad from multiqueue");
        return NULL;
      }
    
      if ((gst_pad_link_full (pad, sinkpad,
                  GST_PAD_LINK_CHECK_NOTHING) != GST_PAD_LINK_OK)) {
        GST_ERROR_OBJECT (dbin, "Couldn't link demuxer and multiqueue");
        goto error; // DTOR-Call
      }
    
      it = gst_pad_iterate_internal_links (sinkpad);
    
      if (!it || (gst_iterator_next (it, &item)) != GST_ITERATOR_OK
          || ((srcpad = g_value_dup_object (&item)) == NULL)) {
        GST_ERROR_OBJECT (dbin,
            "Couldn't get srcpad from multiqueue for sinkpad %" GST_PTR_FORMAT,
            sinkpad);
        goto error; // DTOR-Call
      }
      CHAIN_MUTEX_LOCK (group->parent);
      group->reqpads = g_list_prepend (group->reqpads, gst_object_ref (sinkpad));
      CHAIN_MUTEX_UNLOCK (group->parent); // DTOR-Call
    
    // DTOR-Implementierung {
    beach: 
      if (G_IS_VALUE (&item))             
        g_value_unset (&item);
      if (it)
        gst_iterator_free (it);
      gst_object_unref (sinkpad);
    // }
      return srcpad;
    
    // DTOR-Implementierung {
    error:
      gst_element_release_request_pad (group->multiqueue, sinkpad);
      goto beach;
    // }
    }
    

    Das erste, was mir auffällt ist, dass 11 von 53 Zeilen dieser Funktion, also etwa 20% des Codes direkt oder indirekt dem Aufräum-Code zuzuordnen sind.
    Das ist nicht nur mehr Tipparbeit, sondern erschwert das Codeverständnis für außenstehende und hat mehr Zeilen in die man Fehler einbauen kann.

    Zweitens, auch wenn das hier noch ein sehr harmloses Beispiel ist, so vergleiche man z.B. Zeile 21 mit Zeile 27. Hier muss der Programmierer zusätzlichen
    Gehirnschmalz nur fürs Aufräumen investieren: Da in Zeile 27 der Referenzzähler für sinkpad anders als in Zeile 21 inkrementiert wurde ist hier statt eines
    simplen return NULL zusätzlicher Aufräumcode notwendig, der zentral in den error / beach -Blöcken behandelt wird. Wenn die Programmlogik etwas komplexer
    wird, kann man da schnell etwas übersehen, das eigentlich freigegeben werden müsste - ganz abgesehen davon dass in Zeile 27 noch nichts mit it und item
    gemacht wurde, der Aufräumcode aber dennoch in Zeilen 45 und 47 prüft, ob diese nicht vielleicht freigegeben werden müssen. Das ist nicht nur zusätzlich
    fehleranfällig, sondern eventuell auch noch ineffizient.

    Der dritte Punkt ist etwas weniger offensichtlich, aber etwas das ich bei der Arbeit mit solchen Bibliotheken immer als vermeidbaren Mehraufwand und zusätzliche
    Fehlerquelle empfand: Ob eine aufgerufene Funktion z.B. den Referenzzähler für ein Objekt selbständig erhöht und man selbst für das dekrementieren des
    selbigen zuständig ist, ob eine zurückgegebene Ressource selbst wieder freigegeben werden muss, oder ob eine Funktion, der man ein Objekt übergibt, den
    "Besitz" dieses Objekts übernimmt (Ressourcen müssen nicht selbst freigegeben werden oder Referenzzähler nicht dekrementiert werden), ist alleinig der
    Dokumentation zu entnehmen. So muss man während der Entwicklung sehr viel Aufwand und gewissenhafte Arbeit in die Struktur des eigenen Aufräumcodes
    stecken, wenn man korrekten Speicherleck- und double-free-freien Code schreiben will.

    Das sind alles Dinge, die mit automatischen Destruktoren nahezu komplett wegfallen, bzw. nur ein einziges Mal Arbeit machen. Das hat nichts mit Deppenschutz
    oder Unfähigkeit zu tun, sondern vor allem mit in meinen Augen verschwendeter Arbeitszeit, die ich persönlich lieber in die eigentlichen Algorithmen investiere.

    Gruss,
    Finnegan



  • Was ich noch nicht ganz verstehe: Mir wurde immer erklärt, dass C++ sozusagen die Erweiterung von C ist, insbesondere die Erweiterung um die Konzepte der Objektorientierung. Wenn man aber nun mit C objektorientiert programmieren kann, wozu brauche ich dann noch C++?

    Das soll keine Sprengstoff für Streitgespräche sein, sondern wirklich nur eine naive Frage eines Anfängers.

    Und meine Frage, ob ihr das Buch "The C Programming Language" von Ritchie und Kernighan für empfehlenswert haltet, möchte ich auch noch ebantwortet haben 😃 Habe in das Buch gerade reingeschnuppert, scheint in meinen Augen im Gegensatz zu den Kritiken anderer doch sehr einsteigerfreundlich zu sein.


  • Mod

    Dexter1997 schrieb:

    Was ich noch nicht ganz verstehe: Mir wurde immer erklärt, dass C++ sozusagen die Erweiterung von C ist, insbesondere die Erweiterung um die Konzepte der Objektorientierung. Wenn man aber nun mit C objektorientiert programmieren kann, wozu brauche ich dann noch C++?

    C++ bietet nativen Support für einige Konzepte, mit denen man objektorientiert Programmieren kann. In C muss man mit relativ viel Code das machen, was in C++ ein paar Zeichen sind. Technisch gesehen passiert aber in beiden Fällen das gleiche.

    Ansonsten solltest du C++ nicht so sehr als Erweiterung von C ansehen, sondern als eine ganz andere Sprache, die eine gemeinsame Wurzel mit C hat (bzw. deren Wurzel C ist). Wissen über C nützt dir in C++ herzlich wenig und umgekehrt. Außer du hast ganz viel Wissen über beide Sprachen, dann kannst du super erkennen, wie z.B. die ganze Objektorientierung in C++ "nur" Syntaxzucker ist, mit dem man kurz und einfach schreiben kann, was man sonst in C mit mehr Umständen machen würde.


Anmelden zum Antworten