"Wenn du wirklich OO-Zucker willst, dann benutze C ++", war die sofortige Antwort, die ich von einem meiner Freunde erhielt, als ich danach fragte. Ich weiß, dass zwei Dinge hier absolut falsch sind. Erstens ist OO NICHT 'Zucker' und zweitens hat C ++ NICHT C absorbiert.
Wir müssen einen Server in C schreiben (das Front-End, auf das Python zugreifen soll), und deshalb suche ich nach besseren Möglichkeiten, um große C-Programme zu verwalten.
Die Modellierung eines großen Systems in Bezug auf Objekte und Objektinteraktionen macht es einfacher zu verwalten, zu warten und zu erweitern. Wenn Sie jedoch versuchen, dieses Modell in C zu übersetzen, das keine Objekte (und alles andere davon) enthält, stehen Sie vor einigen wichtigen Entscheidungen.
Erstellen Sie eine benutzerdefinierte Bibliothek, um die OO-Abstraktionen bereitzustellen, die Ihr System benötigt? Dinge wie Objekte, Kapselung, Vererbung, Polymorphismus, Ausnahmen, Pub / Sub (Ereignisse / Signale), Namespaces, Introspection usw. (zum Beispiel GObject oder COS ).
Oder Sie verwenden einfach die grundlegenden C-Konstrukte ( struct
und Funktionen), um alle Ihre Objektklassen (und andere Abstraktionen) auf Ad-hoc-Weise zu approximieren. (zum Beispiel einige der Antworten auf diese Frage auf SO )
Der erste Ansatz gibt Ihnen eine strukturierte Möglichkeit, Ihr gesamtes Modell in C zu implementieren. Er fügt jedoch auch eine Komplexitätsebene hinzu, die Sie beibehalten müssen. (Denken Sie daran, dass wir die Komplexität reduzieren wollten, indem wir zunächst Objekte verwendeten.)
Ich kenne den zweiten Ansatz nicht und weiß nicht, wie effektiv er bei der Annäherung aller Abstraktionen ist, die Sie möglicherweise benötigen.
Meine einfachen Fragen lauten also: Was sind die Best Practices für die Realisierung eines objektorientierten Entwurfs in C. Denken Sie daran, ich frage nicht, wie ich das tun soll. Dies und diese Fragen sprechen darüber und es gibt sogar ein Buch darüber. Was mich mehr interessiert, sind einige realistische Ratschläge / Beispiele, die die wirklichen Probleme ansprechen, die sich zeigen, wenn Sie dies tun.
Hinweis: Bitte raten Sie nicht, warum C nicht zu Gunsten von C ++ verwendet werden sollte. Wir haben diese Etappe weit hinter uns gelassen.
quelle
extern "C"
und von Python aus verwendet werden kann. Sie können dies manuell tun oder sich dabei von SWIG unterstützen lassen. Der Wunsch nach Python-Frontend ist also kein Grund, C ++ nicht zu verwenden. Das heißt nicht, dass es keine triftigen Gründe gibt, bei C. zu bleibenAntworten:
Von meiner Antwort auf Wie soll ich komplexe Projekte in C strukturieren (nicht OO, sondern Komplexitätsmanagement in C):
Von meiner Antwort auf Was sind die typischen Namenskonventionen für öffentliche und private OO C-Funktionen (ich denke, dies ist eine bewährte Methode):
Darüber hinaus können viele UML-Tools C-Code aus UML-Diagrammen generieren. Ein Open Source ist Topcased .
quelle
Ich denke, Sie müssen OO und C ++ in dieser Diskussion unterscheiden.
Das Implementieren von Objekten ist in C möglich und ziemlich einfach - erstellen Sie einfach Strukturen mit Funktionszeigern. Das ist Ihr "zweiter Ansatz", und ich würde mitmachen. Eine andere Möglichkeit wäre, keine Funktionszeiger in der Struktur zu verwenden, sondern die Datenstruktur in direkten Aufrufen als "Kontext" -Zeiger an die Funktionen zu übergeben. Das ist besser, IMHO, da es besser lesbar und leichter nachvollziehbar ist und das Hinzufügen von Funktionen ermöglicht, ohne die Struktur zu ändern (einfache Vererbung, wenn keine Daten hinzugefügt werden). So implementiert C ++ normalerweise den
this
Zeiger.Der Polymorphismus wird komplizierter, da es keine eingebaute Vererbungs- und Abstraktionsunterstützung gibt. Daher müssen Sie entweder die übergeordnete Struktur in Ihre untergeordnete Klasse aufnehmen oder viele Kopierpasten ausführen einfach. Enorme Anzahl von Fehlern, die darauf warten, passiert zu werden.
Virtuelle Funktionen können auf einfache Weise durch Funktionszeiger erreicht werden, die bei Bedarf auf andere Funktionen verweisen. Dies ist sehr fehleranfällig, wenn diese manuell ausgeführt werden.
In Bezug auf Namespaces, Ausnahmen, Vorlagen usw. - ich denke, wenn Sie auf C beschränkt sind - sollten Sie diese einfach aufgeben. Ich habe daran gearbeitet, OO in C zu schreiben, würde es aber nicht tun, wenn ich die Wahl hätte (an diesem Arbeitsplatz habe ich mich buchstäblich für die Einführung von C ++ eingesetzt und die Manager waren "überrascht", wie einfach es war, es zu integrieren mit dem Rest der C-Module am Ende.).
Es versteht sich von selbst, dass Sie, wenn Sie C ++ verwenden können, C ++ verwenden. Es gibt keinen wirklichen Grund, dies nicht zu tun.
quelle
Im Folgenden finden Sie die Grundlagen zum Erstellen der Objektorientierung in C
1. Erstellen von Objekten und Kapselung
Normalerweise erstellt man ein Objekt wie
object_instance = create_object_typex(parameter);
Methoden können hier auf eine der beiden Arten definiert werden.
Beachten Sie, dass in den meisten Fällen
object_instance (or object_instance_private_data)
eine Rückgabe vom Typvoid *.
Die Anwendung kann nicht auf einzelne Member oder Funktionen dieser verweisen.Darüber hinaus verwendet jede Methode diese object_instance für die nachfolgende Methode.
2. Polymorphismus
Wir können viele Funktionen und Funktionszeiger verwenden, um bestimmte Funktionen in der Laufzeit zu überschreiben.
Beispiel: Alle object_methods sind als Funktionszeiger definiert, der sowohl auf öffentliche als auch auf private Methoden erweitert werden kann.
Wir können auch eine Funktionsüberladung in einem begrenzten Sinne anwenden, indem wir eine verwenden,
var_args
die der in printf definierten variablen Anzahl von Argumenten sehr ähnlich ist. Ja, das ist in C ++ nicht so flexibel - aber das ist der naheliegendste Weg.3. Vererbung definieren
Die Definition der Vererbung ist etwas schwierig, aber mit Strukturen kann man Folgendes tun.
hier ist das
doctor
undengineer
von der Person abgeleitet und es ist möglich, es auf eine höhere Ebene zu tippen, sagen wirperson
.Das beste Beispiel hierfür ist GObject und daraus abgeleitete Objekte.
4. Erstellen virtueller Klassen Ich zitiere ein reales Beispiel aus einer Bibliothek namens libjpeg, die von allen Browsern für die JPEG-Dekodierung verwendet wird. Es wird eine virtuelle Klasse mit dem Namen error_manager erstellt, die von der Anwendung erstellt und zurückgegeben werden kann.
Beachten Sie hier, dass JMETHOD in einem Funktionszeiger durch ein Makro erweitert wird, das jeweils mit den richtigen Methoden geladen werden muss.
Ich habe versucht, so viele Dinge zu sagen, ohne zu viele individuelle Erklärungen. Aber ich hoffe, die Leute können ihre eigenen Sachen ausprobieren. Meine Absicht ist es jedoch nur zu zeigen, wie die Dinge sich abbilden.
Es wird auch viele Argumente geben, dass dies nicht genau die wahre Eigenschaft des C ++ - Äquivalents ist. Ich weiß, dass OO in C seiner Definition nach nicht so streng sein wird. Aber wenn man so arbeitet, versteht man einige der Grundprinzipien.
Wichtig ist nicht, dass OO so streng ist wie in C ++ und JAVA. Es ist so, dass man den Code strukturell unter Berücksichtigung des OO-Denkens organisieren und auf diese Weise betreiben kann.
Ich würde den Leuten dringend empfehlen, das wahre Design von libjpeg und die folgenden Ressourcen zu sehen
ein. Objektorientierte Programmierung in C
b. Dies ist ein guter Ort, an dem Menschen Ideen austauschen.
c. und hier ist das ganze Buch
quelle
Die Objektorientierung besteht aus drei Dingen:
1) Modularer Programmaufbau mit autonomen Klassen.
2) Datenschutz mit privater Kapselung.
3) Vererbung / Polymorphismus und verschiedene andere hilfreiche Syntax wie Konstruktoren / Destruktoren, Vorlagen usw.
1 ist bei weitem am wichtigsten, und es ist auch völlig sprachunabhängig, es dreht sich alles um Programmgestaltung. In C erstellen Sie dazu autonome "Codemodule", die aus einer .h-Datei und einer .c-Datei bestehen. Betrachten Sie dies als das Äquivalent einer OO-Klasse. Sie können anhand des gesunden Menschenverstands, der UML oder der für C ++ - Programme verwendeten OO-Entwurfsmethode entscheiden, was in dieses Modul eingefügt werden soll.
2 ist auch sehr wichtig, um nicht nur vor dem absichtlichen Zugriff auf private Daten zu schützen, sondern auch vor unbeabsichtigtem Zugriff, dh "Unordnung im Namespace". C ++ erledigt dies auf elegantere Weise als C, aber es kann immer noch in C mit dem Schlüsselwort static erreicht werden. Alle Variablen, die Sie in einer C ++ - Klasse als privat deklariert hätten, sollten in C als statisch deklariert und im Dateibereich abgelegt werden. Sie sind nur aus ihrem eigenen Codemodul (Klasse) zugänglich. Sie können "setters / getters" genau wie in C ++ schreiben.
3 ist hilfreich, aber nicht notwendig. Sie können OO-Programme ohne Vererbung oder ohne Konstruktoren / Destruktoren schreiben. Diese Dinge sind nett zu haben, sie können sicherlich die Programme eleganter und vielleicht auch sicherer machen (oder das Gegenteil, wenn sie unachtsam verwendet werden). Sie sind aber nicht notwendig. Da C keine dieser hilfreichen Funktionen unterstützt, müssen Sie einfach darauf verzichten. Konstruktoren können durch Init / Destruct-Funktionen ersetzt werden.
Die Vererbung kann durch verschiedene Tricks von struct erfolgen, aber ich würde davon abraten, da dies Ihr Programm wahrscheinlich nur komplexer macht, ohne dass es zu Gewinn kommt (die Vererbung sollte im Allgemeinen sorgfältig angewendet werden, nicht nur in C, sondern in einer beliebigen Sprache).
Schließlich jeder OO Trick kann aus den frühen 90er Jahren erweist sich diese in C. Axel-Tobias Schreiner Buch „Objektorientierte Programmierung mit ANSI C“ erfolgen. Ich würde dieses Buch jedoch niemandem empfehlen: Es fügt Ihren C-Programmen eine unangenehme, seltsame Komplexität hinzu, die sich einfach nicht lohnt. (Das Buch ist hier kostenlos erhältlich, auch wenn ich Sie gewarnt habe.)
Also ist mein Rat, 1) und 2) oben zu implementieren und den Rest zu überspringen. Es ist eine Methode, C-Programme zu schreiben, die sich seit mehr als 20 Jahren bewährt hat.
quelle
Das Schreiben einer dynamischen, polymorphen OO-Fähigkeit in C ist nicht allzu schwierig, da es sich nach 25 Jahren immer noch als schnell und einfach zu handhaben herausstellt. Wenn Sie jedoch eine Objektfunktion im Objective-C-Stil implementieren, ohne die Sprachsyntax zu erweitern, ist der Code, mit dem Sie enden, ziemlich chaotisch:
objc_msgSend(object, selector, …)
. Wenn es weiß, zu welcher Klasse das Objekt gehört, kann es die Implementierung finden, die dem Selektor entspricht, und so die richtige Funktion ausführen.Dies ist alles Teil einer universellen OO-Bibliothek, die es mehreren Entwicklern ermöglicht, die Klassen des jeweils anderen zu verwenden und zu erweitern. Dies kann für Ihr eigenes Projekt zu viel sein. Ich habe C-Projekte oft als "statische" klassenorientierte Projekte mit Strukturen und Funktionen entworfen: - Jede Klasse ist die Definition einer C-Struktur, die das IVAR-Layout spezifiziert - jede Instanz ist nur eine Instanz der entsprechenden Struktur - Objekte können nicht sein "messaged", aber methodenähnliche Funktionen, die so aussehen,
MyClass_doSomething(struct MyClass *object, …)
sind definiert. Dies macht die Dinge klarer im Code als der ObjC-Ansatz, hat aber weniger Flexibilität.Der Kompromiss hängt von Ihrem eigenen Projekt ab: Es hört sich so an, als würden andere Programmierer Ihre C-Schnittstellen nicht verwenden, sodass die Wahl auf die internen Einstellungen zurückzuführen ist. Wenn Sie sich für etwas wie die objc-Laufzeitbibliothek entscheiden, gibt es natürlich plattformübergreifende objc-Laufzeitbibliotheken, die gewartet werden.
quelle
GObject verbirgt keine Komplexität und führt eine eigene Komplexität ein. Ich würde sagen, dass die Ad-hoc-Sache einfacher ist als GObject, es sei denn, Sie benötigen die fortgeschrittenen GObject-Dinge wie Signale oder die Schnittstellen-Maschinerie.
Bei COS ist das etwas anders, da dies mit einem Präprozessor geliefert wird, der die C-Syntax mit einigen OO-Konstrukten erweitert. Es gibt einen ähnlichen Präprozessor für GObject, den G Object Builder .
Sie können auch die Programmiersprache Vala ausprobieren , eine vollständige Hochsprache, die in C kompiliert wird und die die Verwendung von Vala-Bibliotheken aus einfachem C-Code ermöglicht. Es kann entweder GObject, sein eigenes Objekt-Framework oder die Ad-hoc-Methode (mit eingeschränkten Funktionen) verwenden.
quelle
Zunächst einmal, es sei denn, dies ist eine Hausaufgabe oder es gibt keinen C ++ - Compiler für das Zielgerät. Ich glaube nicht, dass Sie C verwenden müssen. Sie können eine C-Schnittstelle mit C-Verknüpfung aus C ++ einfach genug bereitstellen.
Zweitens würde ich untersuchen, inwieweit Sie sich auf Polymorphismus und Ausnahmen verlassen und welche anderen Funktionen die Frameworks bieten können, wenn es sich nicht um einfache Strukturen mit verwandten Funktionen handelt Das Design benötigt sie, beißt dann in die Kugel und verwendet ein Framework, damit Sie die Funktionen nicht selbst implementieren müssen.
Wenn Sie noch kein Design haben, um die Entscheidung zu treffen, machen Sie einen Spike und sehen Sie, was der Code Ihnen sagt.
Letztendlich muss es nicht die eine oder andere Wahl sein (wenn Sie von Anfang an auf Framework setzen, können Sie sich auch daran halten), es sollte möglich sein, mit einfachen Strukturen für die einfachen Teile zu beginnen und nur Bibliotheken hinzuzufügen wie benötigt.
EDIT: Es ist also eine Entscheidung des Managements, den ersten Punkt zu ignorieren.
quelle