Es scheint, dass C seine eigenen Quasi-Objekte wie "Strukturen" hat, die als Objekte betrachtet werden können (auf der höheren Ebene, wie wir es normalerweise denken würden).
Und auch C-Dateien selbst sind grundsätzlich separate "Module", oder? Sind Module dann nicht auch so etwas wie 'Objekte'? Ich bin verwirrt, warum C, das C ++ so ähnlich zu sein scheint, als "prozedurale" Sprache auf niedriger Ebene betrachtet wird, während C ++ auf hoher Ebene "objektorientiert" ist.
* edit: (Klarstellung) warum und wo wird die Linie gezogen, für was ist ein 'Objekt' und was nicht?
object-oriented
procedural
Dunkler Templer
quelle
quelle
Antworten:
Lassen Sie uns gemeinsam die Wikipedia-Seite zur objektorientierten Programmierung lesen und die Merkmale von C-Stil-Strukturen abhaken, die dem entsprechen, was traditionell als objektorientierter Stil angesehen wird:
Bestehen C-Strukturen aus Feldern und Methoden mit ihren Wechselwirkungen ? Nein.
Machen C-Strukturen all diese Dinge auf "erstklassige" Weise? Nein, die Sprache wirkt sich bei jedem Schritt gegen Sie aus.
Tun C structs dies? Nein.
Tun C structs dies? Ja.
Nein.
Kann eine Struktur selbst Nachrichten senden und empfangen? Kann es Daten verarbeiten? Nein.
Passiert das in C? Nein.
Gibt es eines dieser Merkmale von C-Strukturen? Nein.
Welche Eigenschaften von Strukturen halten Sie für "objektorientiert"? Weil ich nichts anderes finden kann als die Tatsache, dass Strukturen Typen definieren .
Jetzt können Sie natürlich Strukturen mit Feldern erstellen, die auf Funktionen verweisen. Sie können Strukturen mit Feldern versehen, die Zeiger auf Arrays von Funktionszeigern sind, die virtuellen Methodentabellen entsprechen. Und so weiter. Sie können natürlich C ++ in C emulieren . Aber das ist eine sehr unidiomatische Art, in C zu programmieren. Sie wären besser dran, wenn Sie nur C ++ verwenden.
An welche Eigenschaften von Modulen denken Sie, dass sie sich wie Objekte verhalten? Unterstützen Module Abstraktion, Kapselung, Messaging, Modularität, Polymorphismus und Vererbung?
Abstraktion und Verkapselung sind ziemlich schwach. Offensichtlich sind Module modular aufgebaut; Deshalb heißen sie Module. Messaging? Nur in dem Sinne, dass ein Methodenaufruf eine Nachricht ist und Module Methoden enthalten können. Polymorphismus? Nee. Erbe? Nee. Module sind ziemlich schwache Kandidaten für "Objekte".
quelle
Das Schlüsselwort ist "orientiert", nicht "Objekt". Selbst C ++ - Code, der Objekte verwendet, diese aber wie Strukturen verwendet, ist nicht objektorientiert .
C und C ++ können beide OOP ausführen (abgesehen von keiner Zugriffskontrolle in C), aber die Syntax dafür ist unpraktisch (gelinde gesagt), während die Syntax in C ++ es sehr einladend macht. C ist prozedural orientiert, während C ++ trotz nahezu identischer Kernfähigkeiten in dieser Hinsicht objektorientiert ist.
Code, der Objekte zum Implementieren von Designs verwendet, die nur mit Objekten ausgeführt werden können (was normalerweise bedeutet, dass der Polymorphismus ausgenutzt wird), ist objektorientierter Code. Code, der Objekte so wenig wie Datenmengen verwendet, selbst wenn Vererbung in einer objektorientierten Sprache verwendet wird, ist eigentlich nur prozeduraler Code, der komplizierter ist, als er sein muss. Code in C, der Funktionszeiger verwendet, die zur Laufzeit mit Strukturen voller Daten geändert werden, führt zu einem Polymorphismus und kann sogar in einer prozeduralen Sprache als "objektorientiert" bezeichnet werden.
quelle
Basierend auf den höchsten Prinzipien:
Ein Objekt ist eine Verkapselung von Daten und Verhalten auf eine miteinander verknüpfte Art und Weise, so dass sie als Ganzes funktionieren, das mehrere Male instanziiert und als Black Box bearbeitet werden kann, wenn Sie die externe Schnittstelle kennen.
Strukturen enthalten Daten, aber kein Verhalten und können daher nicht als Objekte betrachtet werden.
Module enthalten sowohl Verhalten als auch Daten, sind jedoch nicht so eingekapselt, dass beide in Beziehung stehen und mit Sicherheit nicht mehrmals instanziiert werden können.
Und das ist, bevor Sie auf Vererbung und Polymorphismus kommen ...
quelle
"structs" sind nur Daten. Der übliche schnelle und schmutzige Test der "Objektorientierung" lautet: "Gibt es eine Struktur, mit der Code und Daten als eine Einheit gekapselt werden können?". C scheitert daran und ist daher prozedural. C ++ besteht diesen Test.
quelle
this
Zeiger, aber in diesem Fall sind Daten und Mittel zur Datenverarbeitung in der Struktur gekapselt.#include
hinein d und minus etwas durch die nicht bedingt eingeschlossen entfernt wird (#if
,#ifdef
und dergleichen).C, wie C ++, hat die Fähigkeit der Bereitstellung von Datenabstraktion , die eine Sprache der objektorientierten Programmierung Paradigma ist , die vor ihm existierte.
OOP in C ++ erweitert die Möglichkeiten, Daten zu abstrahieren. Einige sagen, dass es schädlich ist , während andere es als ein gutes Werkzeug betrachten, wenn es richtig verwendet wird.
Sie werden jedoch viele C-Hacker finden, die predigen, dass C genau die richtige Menge an Abstraktion kann und dass der von C ++ verursachte Overhead sie nur davon abhält, das eigentliche Problem zu lösen.
Andere neigen dazu, es ausgewogener zu betrachten und sowohl die Vorteile als auch die Nachteile zu akzeptieren.
quelle
Sie müssen sich die andere Seite der Medaille ansehen: C ++.
In OOP denken wir an ein abstraktes Objekt (und entwerfen das Programm entsprechend), zum Beispiel ein Auto, das anhalten, beschleunigen, nach links oder rechts abbiegen kann usw. Eine Struktur mit einem Bündel von Funktionen passt einfach nicht zum Konzept.
Bei "echten" Objekten müssen wir zum Beispiel die Mitglieder verbergen, oder wir können auch eine Vererbung mit einer echten "ist eine" Beziehung und vielem mehr haben.
NACH DEM LESEN DER KOMMENTARE: Nun, es ist richtig, dass (fast) alles mit C gemacht werden kann (das ist immer wahr), aber auf den ersten Blick dachte ich, dass das, was c von c ++ trennt, die Art ist, wie Sie beim Entwerfen eines Programms denken.
Das einzige, was wirklich den Unterschied ausmacht, ist das Auferlegen von Richtlinien durch den Compiler . dh rein virtuelle Funktion, und so weiter. Diese Antwort wird sich jedoch nur auf technische Probleme beziehen, aber ich denke, der Hauptunterschied (wie erwähnt) ist die ursprüngliche Art und Weise, wie Sie beim Codieren denken, da C ++ Ihnen eine besser eingebaute Syntax für solche Dinge bietet, anstatt OOP in zu tun ein etwas ungeschickter Weg in C.
quelle
Du hast es selbst gesagt. Während C Dinge hat, die Objekten ähneln, sind sie immer noch keine Objekte, und deshalb wird C nicht als OOP-Sprache betrachtet.
quelle
Objektorientiert bezieht sich sowohl auf ein Architekturmuster (oder sogar ein Metamuster) als auch auf die Sprachen, die Funktionen enthalten, die bei der Implementierung oder Verwendung dieses Musters hilfreich sind.
Sie können ein "OO" -Design implementieren (Gnome-Desktop ist vielleicht das beste Beispiel für OO in reinem C). Ich habe dies sogar mit COBOL gesehen!
In der Lage zu sein, eine OO-Entwurfsdosis zu implementieren, macht die Sprache OO jedoch nicht. Puristen würden argumentieren, dass Java und C ++ nicht wirklich OO sind, da Sie die grundlegenden "Typen" wie "int" und "char" nicht überschreiben oder erben können und Java die Mehrfachvererbung nicht unterstützt. Da sie jedoch die am häufigsten verwendeten OO-Sprachen sind und die meisten der Paradigmen unterstützen, werden sie von den meisten "echten" Programmierern, die für die Erstellung von Arbeitscode bezahlt werden, als OO-Sprachen betrachtet.
C hingegen unterstützt nur Strukturen (wie COBOL, Pascal und Dutzende anderer prozeduraler Sprachen). Sie könnten argumentieren, dass Mehrfachvererbung dadurch unterstützt wird, dass Sie jede Funktion für jedes Datenelement verwenden können, aber die meisten würden dies eher als Fehler ansehen als eine Funktion.
quelle
Schauen wir uns einfach die Definition von OO an:
C bietet keine dieser drei. Insbesondere wird Messaging nicht bereitgestellt , was das wichtigste ist.
quelle
static
) Funktionen und Datenelemente haben können.Es gibt eine Reihe von Hauptbestandteilen für OO, aber die großen sind, dass die Mehrheit des Codes nicht weiß, was sich in einem Objekt befindet (sie sehen die Oberflächenschnittstelle, nicht die Implementierung), dass der Status eines Objekts eine verwaltete Einheit ist (dh, wenn das Objekt aufhört zu sein, so tut dies auch sein Zustand), und wenn ein Code eine Operation an einem Objekt aufruft, tun sie dies, ohne genau zu wissen, was diese Operation ist oder beinhaltet (alles, was sie tun, ist einem Muster zu folgen, um a zu werfen) "Nachricht" über die Mauer).
C macht die Verkapselung ganz gut; Code, der die Definition einer Struktur nicht erkennt, kann (zu Recht) nicht in sie hineinsehen. Dazu müssen Sie lediglich eine Definition wie diese in eine Header-Datei einfügen:
Natürlich wird eine Funktion benötigt, die
Foo
s erstellt (dh eine Factory) und die einen Teil der Arbeit an das zugewiesene Objekt selbst delegiert (dh über eine "Konstruktor" -Methode) und auch eine Möglichkeit hat das Objekt wieder zu entsorgen (während es über seine "Destruktor" -Methode bereinigt wird), aber das sind Details.Der Methodenversand (dh das Versenden von Nachrichten) kann auch über die Konvention erfolgen, dass das erste Element der Struktur tatsächlich ein Zeiger auf eine Struktur voller Funktionszeiger ist und dass jeder dieser Funktionszeiger ein
Foo
als erstes Argument verwenden muss. Beim Versand muss dann die Funktion nachgeschlagen und mit dem richtigen Argument umgeschrieben werden, was mit einem Makro und ein wenig List nicht so schwer zu tun ist . (Diese Funktionstabelle ist der Kern dessen, was eine Klasse in einer Sprache wie C ++ wirklich ist.)Darüber hinaus bedeutet dies auch eine späte Bindung: Der Versandcode weiß nur, dass er einen bestimmten Offset in einer Tabelle aufruft, auf die das Objekt zeigt. Dies muss nur während der Zuweisung und Initialisierung des Objekts eingestellt werden. Es ist möglich, mit noch komplexeren Versandschemata zu arbeiten, die Ihnen mehr Laufzeitdynamik (zu einem Preis von Geschwindigkeit) verschaffen, aber sie sind Kirschen über dem Grundmechanismus.
Dies bedeutet jedoch nicht, dass C eine OO-Sprache ist. Der Schlüssel ist, dass C Sie die ganze knifflige Arbeit, die Konventionen und den Versandmechanismus selbst zu schreiben, überlässt (oder eine Bibliothek von Drittanbietern verwendet). Das ist viel Arbeit. Es bietet auch keine syntaktische oder semantische Unterstützung, so dass die Implementierung eines vollständigen Klassensystems (mit Dingen wie Vererbung) unnötig schmerzhaft wäre. Wenn Sie sich mit einem komplexen Problem befassen, das durch ein OO-Modell gut beschrieben wird, ist eine OO-Sprache beim Schreiben der Lösung sehr hilfreich. Die zusätzliche Komplexität kann gerechtfertigt sein.
quelle
Ich denke, C ist vollkommen in Ordnung und anständig, um objektorientierte Konzepte zu implementieren, achselzucken . Die meisten meiner Ansicht nach bestehenden Unterschiede zwischen der Untergruppe der objektorientierten Sprachen mit gemeinsamem Nenner sind aus meiner pragmatischen Sicht geringfügig und syntaktisch.
Beginnen wir zum Beispiel mit dem Verstecken von Informationen. In C können wir dies erreichen, indem wir einfach die Definition einer Struktur verbergen und mit undurchsichtigen Zeigern arbeiten. Das modelliert effektiv die
public
vs.private
Unterscheidung von Datenfeldern, wie wir sie mit Klassen erhalten. Und es ist einfach genug und kaum anti-idiomatisch, da die Standard-C-Bibliothek in hohem Maße darauf angewiesen ist, Informationen zu verbergen.Natürlich verlieren Sie die Möglichkeit, die genaue Speicherzuordnung der Struktur mithilfe von undurchsichtigen Typen einfach zu steuern, aber das ist nur ein bemerkenswerter Unterschied zwischen beispielsweise C und C ++. C ++ ist definitiv ein überlegenes Werkzeug, wenn es darum geht, objektorientierte Konzepte über C zu programmieren und dabei die Kontrolle über Speicherlayouts zu behalten. Dies bedeutet jedoch nicht unbedingt, dass Java oder C # C in dieser Hinsicht überlegen sind, da diese beiden Funktionen Sie überzeugen verlieren vollständig die Fähigkeit zu steuern, wo Objekte im Speicher zugeordnet sind.
Und wir müssen eine Syntax verwenden,
fopen(file, ...); fclose(file);
die sich von der vonfile.open(...); file.close();
Big Whoop unterscheidet. Wen interessiert das schon? Vielleicht nur jemand, der sich in seiner IDE stark auf die automatische Vervollständigung stützt. Ich gebe zu, dass dies aus praktischer Sicht eine sehr nützliche Funktion sein kann, aber möglicherweise keine, die eine Diskussion darüber erfordert, ob eine Sprache für OOP geeignet ist.Uns fehlt die Fähigkeit,
protected
Felder effektiv umzusetzen . Ich werde dort total einreichen. Ich glaube jedoch nicht, dass es eine konkrete Regel gibt, die besagt: " Alle OO-Sprachen sollten über eine Funktion verfügen, die es Unterklassen ermöglicht, auf Mitglieder einer Basisklasse zuzugreifen, auf die normale Clients immer noch nicht zugreifen sollten ." Außerdem sehe ich selten Anwendungsfälle für geschützte Mitglieder, die zumindest nicht ein bisschen misstrauisch sind, eine Wartungshürde zu werden.Und natürlich müssen wir den OO-Polymorphismus mit Tabellen von Funktionszeigern und Zeigern auf sie "emulieren", um einen dynamischen Versand mit etwas mehr Boilerplate zu ermöglichen, um diese analogen
vtables
undvptrs
, aber ein bisschen Boilerplate hat mir nie viel Leid bereitet.Vererbung ist ähnlich. Wir können das leicht durch Komposition modellieren, und bei der internen Arbeitsweise von Compilern läuft es auf dasselbe hinaus. Natürlich verlieren wir die Typensicherheit , wenn wir einen Downcast ausführen möchten , und da würde ich sagen, wenn Sie überhaupt einen Downcast ausführen möchten , verwenden Sie bitte kein C dafür, da die Dinge, die die Leute in C tun, um einen Downcast zu emulieren, fürchterlich sein können Sicherheitsstandpunkt, aber ich möchte, dass die Leute überhaupt nicht niedergeschlagen sind . Typensicherheit ist etwas, das Sie in C leicht übersehen können, da der Compiler so viel Spielraum bietet, um Dinge wie Bits und Bytes zu interpretieren, was die Fähigkeit einbüßt, mögliche Fehler zur Kompilierungszeit abzufangen, aber einige Sprachen als objektorientiert betrachtet werden nicht einmal statisch getippt.
Also keine Ahnung, ich denke es ist in Ordnung. Natürlich würde ich C nicht verwenden, um eine große Codebasis zu erstellen, die den SOLID-Prinzipien entspricht, aber dies liegt nicht unbedingt an den Mängeln in der objektorientierten Front. Viele der Funktionen, die ich vermissen würde, wenn ich versuchen würde, C für einen solchen Zweck zu verwenden, würden mit Sprachfunktionen zusammenhängen, die nicht direkt als Voraussetzung für OOP angesehen werden, wie z. B. starke Typensicherheit, Destruktoren, die automatisch aufgerufen werden, wenn Objekte den Gültigkeitsbereich verlassen, Operator Überladung, Vorlagen / Generika und Ausnahmebehandlung. Es ist, wenn ich die Zusatzfunktionen vermisse, die ich für C ++ erreiche.
quelle
foo_create
undfoo_destroy
und ähnelnfoo_clone
, zBstruct Foo
einem solchen Fall müssen wir nur die Invarianten beibehalten , um sicherzustellen, dass die Datenelemente nicht von der Außenwelt abgerufen und mutiert werden können, die von undurchsichtigen Typen (deklariert, aber nicht für die Außenwelt definiert) sofort angegeben wird. Es ist wohl eine stärkere Form des Versteckens von Informationen, die mit derpimpl
in C ++ vergleichbar ist.Keine schlechte Frage.
Wenn Sie c als OO-Sprache bezeichnen, müssen Sie auch so gut wie alle prozeduralen Sprachen OO aufrufen. Es würde den Begriff also bedeutungslos machen. c hat keine Sprachunterstützung für OO. If hat Strukturen, aber Strukturen sind
types
keine Klassen.Eigentlich hat c im Vergleich zu den meisten Sprachen überhaupt nicht viele Funktionen. Es wird hauptsächlich wegen seiner Geschwindigkeit, Einfachheit, Beliebtheit und Unterstützung verwendet, einschließlich Tonnen von Bibliotheken.
quelle