Kämpfen Sie mit C aus dem objektorientierten Land? [closed]

Lesezeit: 7 Minuten

Benutzer-Avatar
KönigNestor

Wenn ich mit Programmierproblemen konfrontiert werde, beginne ich natürlich damit, sie in meinem Kopf in logische Objekte zu zerlegen. Wer trägt welche Verantwortung, wem gehört was, wem kommt was zu, etc.

Ich kämpfe mit C. Ich verstehe einfach nicht, wie man Dinge in einer prozeduralen Sprache macht.

Kann mir ein erfahrener C-Programmierer erklären, wie ich während der Entwurfszeit über meine Programme nachdenken sollte?

Zum Beispiel möchte ich meine eigene Semaphore-Klasse schreiben. Ich bräuchte für mein Programm natürlich eine Queue-Datenstruktur, die ich auch selbst schreiben möchte. Wenn ich dies in Java oder C# tun müsste, könnte ich einfach eine schnelle Queue-Klasse erstellen und eine neue Instanz davon in meiner Semaphore-Klasse erstellen.

Aber in C gibt es keine Objekte. Muss ich also das gesamte Verhalten meiner Warteschlangen-Datenstruktur einbetten?

Kann mir jemand helfen “es zu bekommen”?

Verwandt: Was ist der beste Weg, um die Entwicklung einer Anwendung in c zu planen und zu organisieren?

Benutzer-Avatar
S. Lott

Aber in C gibt es keine Objekte. Muss ich also das gesamte Verhalten meiner Warteschlangen-Datenstruktur einbetten?

Nein.

Mach das.

  1. Definieren Sie Ihre Klasse so, wie Sie sich beim OO-Design wohlfühlen.

  2. Schreiben Sie die Attribute Ihrer Klasse als C-Struktur.

  3. Fügen Sie diese Struktur zusammen mit allen Funktionen, die mit dieser Struktur arbeiten, in eine Header-Datei ein. Stellen Sie sicher, dass a MyStruct * self ist das erste Argument für all diese “Methodenfunktionen”.

  4. Schreiben Sie ein C-Modul mit allen Körpern der Methodenfunktionen.

OO der armen Person in C. Es funktioniert gut. Seien Sie einfach diszipliniert, wenn Sie alles, was Sie brauchen, in die Struktur einfügen – öffentliche und private Instanzvariablen – alles.

Vermeiden Sie im Allgemeinen den Versuch, überhaupt private Variablen zu haben. Sie verfügen nicht über die volle Leistungsfähigkeit eines OO-Compilers, also stören Sie sich nicht an geringwertigen Features wie „private“ oder „protected“.

  • führende Unterstriche sind reserviert und sollten nicht verwendet werden.

    – Evan Teran

    23. März 2009 um 19:10 Uhr

  • Je nachdem, wie Ihre API definiert ist, können Sie auch Variationen von PIMPL verwenden, um private Variablen zu definieren, oder umzuwandeln zu/von “char reserviert[SOME_SIZE]”, sodass der Benutzer niemals sehen kann, was die privaten Variablen sind.

    – Greg Rogers

    23. März 2009 um 19:11 Uhr

  • @evan-Namen mit führenden Unterstrichen sind nur im globalen Bereich reserviert, nicht für Strukturmitglieder

    anon

    23. März 2009 um 19:15 Uhr

  • Guter Punkt. Wenn Sie OO pro Person machen, sind Dinge wie privat und geschützt mehr Ärger als sie wert sind.

    – S. Lott

    23. März 2009 um 19:59 Uhr

  • Ich denke, private/public könnte mit #define und #undef um Schnittstellendeklarationen herum emuliert werden.

    – Brian R. Bondy

    23. März 2009 um 20:03 Uhr

Benutzer-Avatar
sk.

Ich würde die Antwort von S. Lott ändern, um an zu verwenden undurchsichtiger Zeiger um das Datenverbergen der Mitglieder der Struktur durchzuführen:

  1. Definieren Sie Ihre Klasse, wie Sie wollen, mit normalem OO-Design.
  2. Mitgliedsvariablen Ihrer Klasse gehen in eine C-Sprachstruktur.
  3. In der Header-Datei möchten Sie die Mitgliedsvariablen Ihres Objekts nicht offenlegen (da diese in einer OO-Sprache “privat” wären). Verwenden Sie stattdessen einen undurchsichtigen Zeiger, dh
    typedef struct mystruct_s *mystruct_t; // first argument to all your methods
  4. Fügen Sie für alle Methoden, die “öffentlich” sein sollen, ihre Signaturen in Ihre .h-Datei ein. Methodenkörper sollten in die .c-Datei gehen, und “private” Methoden sollten nur in der .c-Datei definiert und auch als statisch deklariert werden, damit ihre Symbole nicht mit Symbolen kollidieren, die in anderen Dateien definiert sind.

Clevere Namenskonventionen wie Unterstriche sind bei dieser Methode unnötig, aber es bedeutet, dass alle Ihre Mitgliedsvariablen privat sind. Funktionen können öffentlich oder privat sein, obwohl öffentliche Funktionen Teil eines globalen Namensraums sind, so dass Sie ihre Namen vielleicht mit einem “Paket”-Namen wie qualifizieren möchten mystruct_push(), mystruct_pop()etc.

Sie müssen auch klarstellen, ob der Anrufer oder die Bibliothek für den Anruf verantwortlich ist malloc() und free(). Höchstwahrscheinlich werden Sie haben mystruct_t *create() und void destroy(mystruct_t *target) Methoden.

  • Ich stimme zu; Ich hatte für die Antwort von S. Lott gestimmt, aber meine Stimme darauf verschoben, weil das Verbergen des Inhalts der Struktur der beste Weg ist – ich habe genau das viele Male getan und es funktioniert Ja wirklich Gut.

    – Lawrence Dol

    24. März 2009 um 23:09 Uhr

Benutzer-Avatar
Brian R. Bondy

Mit C kann man immer noch objektorientiert denken.

Sie müssen nur eine Struktur und eine Reihe von Funktionen erstellen, die einen Zeiger auf eine Instanz dieser Struktur als ersten Parameter verwenden.

Was den Polymorphismus betrifft, so können Sie die Größe der Struktur als erstes Mitglied der Struktur übergeben, sodass Sie wissen, wie sie umgewandelt wird.

Es gibt eine große pdf der objektorientierten Programmierung mit ANSI-C hier.

Ich hatte eine bärenstarke Zeit, vom prozeduralen zum OO-Denken zu wechseln, also fühle ich Ihren Schmerz.

Ich stellte fest, dass es am besten war, darüber nachzudenken, wie sie für den Anrufer aussehen würden, um zu lernen, wie man Objekte erstellt. Der gleiche Ansatz könnte Ihnen helfen, wenn Sie in die andere Richtung gehen. Überlegen Sie, wie die API zu Ihrer Komponente aussehen würde. Eine gute Möglichkeit besteht darin, vorhandene C-APIs zu studieren (ich habe die Standard-Java-APIs als eine Reihe von Beispielen für eine OO-API verwendet).

Sie sind es gewohnt, eine Warteschlangenkomponente wie die folgende zu verwenden:

import some.package.Queue;

Queue q = new Queue(); 
q.add(item);

In einer typischen C-API würden Sie eher Folgendes erwarten:

#include <queue.h> // provides queue, make_queue(), queue_add(), others

queue q = make_queue(); // queue is probably a struct, or a struct*
queue_add(q,item);

Wann immer Sie in Objekten denken, führen Sie eine ähnliche Transformation durch.

Sie können Zeiger auf Funktionen und ähnliches verwenden, um objektähnliche Strukturen in C zu erstellen – aber viele tausend C-Programmierer sind ohne ausgekommen.

Viel Glück!

Probier das aus Lua-C-API. Es war mein Leitstern, was das Design von C-Schnittstellen angeht. Jede Funktion nimmt einen Lua-Zustand als führendes Argument, das zu Ihrem “dies” wird. Vererbung ist ein wenig kniffliger, aber Streifenhörnchen schafft es ziemlich gut, Funktionen aufzudecken, die eine generische Form annehmen, und die Details herauszufinden, welche Funktion tatsächlich über eine “Klasse” aufgerufen wird. Sie können void* oft ausnutzen, um Funktionen verschiedene Typen (Strukturen) annehmen zu lassen, so wie Sie es in OO überladen würden. Es kann sich manchmal etwas hackish anfühlen, funktioniert aber gut.

Benutzer-Avatar
NVRAM

Ursprünglich war C++ nur ein Compiler, der C-Code aus den C++-Quellen schrieb; Diese wurden dann vom nativen C-Compiler kompiliert, gelinkt usw.

Daher sind alle OOP-Methoden in C verfügbar – es ist nur so, dass der Compiler Ihnen nicht helfen wird und nicht alle Funktionen zur Kompilierzeit wie Vorlagen, Überschreiben von Operatoren, Ausblenden von Daten usw. bietet.

  1. Das C++ “struct” war (wahrscheinlich immer noch) äquivalent zu einer “class” mit allen Membern “public”.
  2. Elementfunktionen können als Funktionszeiger in einer Struktur implementiert werden; dies kann Verkapselung und Polymorphismus bereitstellen. Konstruktoren existieren im globalen Gültigkeitsbereich, es sei denn, Sie verwenden Fabriken. Destruktoren können Mitgliedsfunktionen sein.
  3. Verwenden von Accessor-Member-Funktionen wie getColor(), setColor() kann interne Differenzen mildern und einige Daten verbergen. Wenn Sie wirklich wollen, können Sie den Vorschlag von Brian Bondy verwenden, sich mit Makros zu verstecken.
  4. Es gibt tatsächlich viele FOSS-Bibliotheken, die Standard-Container bereitstellen – Hash-Tabellen, dynamische Arrays, verknüpfte Listen usw.

Benutzer-Avatar
überspringen

Ich stimme den Vorschlägen für “Poor Man’s OO in C” zu. Ich denke auch, dass Sie davon profitieren könnten, wenn Sie sich etwas Zeit nehmen, um zu sehen, wie Perls OO funktioniert. Grundsätzlich wird OO in Perl dadurch erreicht, dass der Interpreter jede Methode mit der Instanz als implizitem ersten Parameter versorgt. Sie sollten das Gleiche explizit in C tun und eine wirklich, wirklich gute Codeorganisation verwenden, da der Compiler keine gute Kapselung für Sie erzwingen wird.

Übrigens können Sie erzwingen, dass die Mitglieder Ihrer Strukturen privat bleiben, indem Sie verwenden undurchsichtige Zeiger. Ich meine mich zu erinnern, dass die GNU-Programmierstandards und -empfehlungen eine Technik enthielten, um dies zu tun, indem im Grunde alles in void* umgewandelt wurde, wenn es herumgereicht wurde, und dann typedefs verwendet wurden, um jeden spezifischen Typ von undurchsichtigen Zeigern zu benennen, die übergeben werden sollten. (dh jede “Klasse”)

1226370cookie-checkKämpfen Sie mit C aus dem objektorientierten Land? [closed]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy