it-swarm-eu.dev

Warum wird C nicht als "objektorientierte" Sprache angesehen?

Es scheint, dass C seine eigenen Quasi-Objekte wie 'Strukturen' hat, die als Objekte betrachtet werden können (auf die Art und Weise auf hoher Ebene, wie wir normalerweise denken würden).

Und auch C-Dateien selbst sind im Grunde separate "Module", oder? Sind Module dann nicht auch wie Objekte? Ich bin verwirrt darüber, warum C, das C++ so ähnlich zu sein scheint, als "prozedurale" Sprache auf niedriger Ebene angesehen wird, während C++ auf hoher Ebene "objektorientiert" ist.

* edit: (Klarstellung) Warum und wo wird die Linie gezogen, für was ein 'Objekt' ist und was nicht?

95
Dark Templar

Es scheint, dass C seine eigenen Quasi-Objekte wie 'Strukturen' hat, die als Objekte betrachtet werden können

Lassen Sie uns gemeinsam Sie und ich die Wikipedia-Seite zur objektorientierten Programmierung durchlesen und die Merkmale von Strukturen im C-Stil abhaken, die dem entsprechen, was traditionell als objektorientierter Stil angesehen wird:

(OOP) ist ein Programmierparadigma, das "Objekte" verwendet - Datenstrukturen, die aus Datenfeldern und Methoden zusammen mit ihren Interaktionen bestehen

Bestehen C-Strukturen aus Feldern und Methoden zusammen mit ihren Wechselwirkungen? Nein.

Programmiertechniken können Merkmale wie Datenabstraktion, Kapselung, Messaging, Modularität, Polymorphismus und Vererbung umfassen.

Tun C-Strukturen eines dieser Dinge "erstklassig"? Nein, die Sprache wirkt bei jedem Schritt gegen Sie.

der objektorientierte Ansatz ermutigt den Programmierer, Daten dort zu platzieren, wo sie für den Rest des Programms nicht direkt zugänglich sind

Tun dies C-Strukturen? Nein.

Ein objektorientiertes Programm enthält normalerweise verschiedene Arten von Objekten, wobei jeder Typ einer bestimmten Art von zu verwaltenden komplexen Daten oder möglicherweise einem realen Objekt oder Konzept entspricht

Tun dies C-Strukturen? Ja.

Man kann sich vorstellen, dass Objekte ihre Daten in eine Reihe von Funktionen einschließen, um sicherzustellen, dass die Daten ordnungsgemäß verwendet werden

Nein.

jedes Objekt kann Nachrichten empfangen, Daten verarbeiten und Nachrichten an andere Objekte senden

Kann eine Struktur selbst Nachrichten senden und empfangen? Kann es Daten verarbeiten? Nein.

OOP-Datenstrukturen neigen dazu, "ihre eigenen Operatoren mit sich herumzutragen".

Passiert das in C? Nein.

Dynamischer Versand ... Kapselung ... Subtyp-Polymorphismus ... Objektvererbung ... Offene Rekursion ... Klassen von Objekten ... Instanzen von Klassen ... Methoden, die auf die angehängten Objekte wirken ... Nachrichtenübergabe ... Abstraktion

Sind einige dieser Merkmale von C-Strukturen? Nein.

Welche Eigenschaften von Strukturen halten Sie für "objektorientiert"? Weil ich keine andere als finden kann die Tatsache, dass Strukturen Typen definieren.

Jetzt können Sie natürlich Strukturen erstellen, deren Felder Zeiger auf Funktionen sind. 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 emulieren C++ in C. Aber das ist eine sehr nicht-idiomatische Art, in C zu programmieren; Sie sind besser dran, wenn Sie nur C++ verwenden.

Außerdem sind C-Dateien im Grunde genommen separate "Module", oder? Sind Module dann nicht auch wie Objekte?

Welche Eigenschaften von Modulen denken Sie, die sie wie Objekte wirken lassen? Unterstützen Module Abstraktion, Kapselung, Messaging, Modularität, Polymorphismus und Vererbung?

Abstraktion und Einkapselung sind ziemlich schwach. Offensichtlich sind Module modular aufgebaut; Deshalb werden sie Module genannt. 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".

104
Eric Lippert

Das Schlüsselwort ist "orientiert", nicht "Objekt". Selbst C++ - Code, der Objekte verwendet, diese jedoch wie Strukturen verwendet, ist kein Objekt orientiert.

C und C++ können beide OOP (abgesehen von keiner Zugriffskontrolle in C)), aber die Syntax dafür in C ist (gelinde gesagt) unpraktisch, während die Syntax in C++ dies ermöglicht sehr einladend. C ist prozedural ausgerichtet, während C++ trotz nahezu identischer Kernfunktionen in dieser Hinsicht auf Objekte ausgerichtet ist.

Code, der Objekte verwendet, um Entwürfe zu implementieren, die nur mit Objekten ausgeführt werden können (was normalerweise bedeutet, den Polymorphismus auszunutzen), ist objektorientierter Code. Code, der Objekte nur mit 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 datenreichen Strukturen geändert werden, führt zu einem gewissen Polymorphismus und kann auch in einer prozedural orientierten Sprache als "objektorientiert" bezeichnet werden.

46
kylben

Basierend auf den höchsten Prinzipien:

Ein Objekt ist eine Verkapselung von Daten und Verhalten auf eine miteinander verknüpfte Weise, sodass sie als Ganzes funktionieren, die mehrfach instanziiert und als Black Box bearbeitet werden können, 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 gekapselt, dass beide miteinander zusammenhängen und sicherlich nicht mehrmals instanziiert werden können.

Und das ist, bevor Sie sich mit Vererbung und Polymorphismus befassen ...

19
Jon Hopkins

"Strukturen" 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.

6
Brian Knoblauch

C hat genau wie C++ die Fähigkeit, Data Abstraction bereitzustellen, was eine Redewendung des zuvor existierenden objektorientierten Programmierparadigmas ist.

  • C-Strukturen können Daten haben (und das ist ihr Hauptzweck)
  • C-Strukturen können auch Funktionszeiger als Daten definieren
  • C-Strukturen können und werden häufig mit einer Reihe von Funktionen verknüpft, genau wie Methoden. Nur der Zeiger this Wird nicht implizit übergeben, aber Sie müssen ihn explizit als erstes Argument für jede entworfene Methode angeben die angegebene Struktur zu behandeln. C++ erledigt dies automatisch für Sie, sowohl wenn Sie definieren als auch aufrufen Klassen-/Strukturmethoden.

OOP in C++ erweitert die Mittel, um Daten zu abstrahieren. Einige sagen, dass es schädlich ist , während andere es für ein gutes Werkzeug halten, wenn es richtig verwendet wird.

  • C++ macht den Zeiger this implizit, indem der Benutzer ihn nicht an "Methoden der Klasse/Struktur" übergeben muss, solange der Typ (zumindest teilweise) identifiziert werden kann.
  • Mit C++ können Sie den Zugriff auf bestimmte Methoden (Klassenfunktionen) einschränken und somit mehr "defensive Programmierung" oder "idiotensicher" ermöglichen.
  • C++ fördert Abstraktionen, indem es durch die Einführung von Eine stärkere Typensicherheit bietet.
    1. Der Operator new Anstelle von malloc + cast
    2. Vorlagen anstelle von ungültigen Zeigern
    3. Inline-Funktionen Typisierte Werte anstelle von Makros empfangen
    4. Eingebauter Polymorphismus, den Sie nicht selbst implementieren müssen, mit dem Sie Abstraktionshierarchien erstellen können, Verträge und = Spezialisierungen.

Es gibt jedoch viele C- "Hacker", die predigen, wie C genau die richtige Menge an Abstraktion kann und wie der durch C++ erzeugte Overhead sie nur von der Lösung des eigentlichen Problems ablenkt.

Ineffiziente abstrahierte Programmiermodelle, bei denen Sie zwei Jahre später feststellen, dass einige Abstraktionen nicht sehr effizient waren, aber jetzt hängt Ihr gesamter Code von allen Nice-Objektmodellen ab, und Sie können ihn nicht reparieren, ohne Ihre App neu zu schreiben. - Linus Torvalds

Andere neigen dazu, es ausgewogener zu betrachten und sowohl die Vor- als auch die Nachteile zu akzeptieren.

C macht es einfach, sich in den Fuß zu schießen; C++ macht es schwieriger, aber wenn Sie es tun, bläst es Ihr ganzes Bein ab. -- Bjarne Stroustrup

5
Yam Marcovic

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 drehen usw. kann. Eine Struktur mit einem Funktionsbündel passt einfach nicht zum Konzept.

Bei "echten" Objekten müssen wir beispielsweise die Mitglieder ausblenden, oder wir können auch eine Vererbung mit einer echten "ist eine" Beziehung und vielem mehr haben.

NACH DEM LESEN DER KOMMENTARE UNTEN : 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 denken beim Entwerfen eines Programms.

Das einzige, was wirklich den Unterschied ausmacht, ist das Auferlegen von Richtlinien durch den Compiler. d.h. reine virtuelle Funktion und so weiter. Diese Antwort bezieht sich jedoch nur auf technische Probleme, 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 auf etwas ungeschickte Weise in C.

2
Guy L

Du hast es irgendwie 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.

1
ryanzec

Objektorientiert bezieht sich sowohl auf ein Architekturmuster (oder sogar ein Metamuster) als auch auf die Sprachen, die Funktionen zur Implementierung oder zum Mandat zur Verwendung dieses Musters enthalten.

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 jedoch nicht zu OO. Puristen würden argumentieren, dass Java und C++ nicht wirklich OO sind, da Sie die grundlegenden "Typen" wie "int" und "char" und Java unterstützt keine Mehrfachvererbung. Da sie jedoch die am häufigsten verwendeten OO Sprachen sind und die meisten Paradigmen unterstützen, betrachten die meisten "echten" Programmierer, die für die Erstellung von Arbeitscode bezahlt werden, sie als OO Sprachen.

C hingegen unterstützt nur Strukturen (wie COBOL, Pascal und Dutzende anderer prozeduraler Sprachen). Sie könnten argumentieren, dass Mehrfachvererbung unterstützt wird, indem Sie jede Funktion für jedes Datenelement verwenden können, aber die meisten würden dies eher als Fehler betrachten als eine Funktion.

1
James Anderson

Ich denke, C ist vollkommen in Ordnung und anständig für die Implementierung objektorientierter Konzepte, Achselzucken. Die meisten Unterschiede, wie ich sie sehe, zwischen der Teilmenge der Sprachen mit gemeinsamem Nenner, die als objektorientiert angesehen werden, sind aus meiner pragmatischen Sicht geringfügig und syntaktisch.

Beginnen wir beispielsweise mit dem Verstecken von Informationen. In C können wir dies erreichen, indem wir einfach die Definition einer Struktur ausblenden und durch undurchsichtige Zeiger damit arbeiten. Dies modelliert effektiv die Unterscheidung von public vs. private von Datenfeldern, wie wir sie mit Klassen erhalten. Und es ist einfach genug und kaum anti-idiomatisch, da die Standard-C-Bibliothek stark darauf angewiesen ist, um Informationen zu verbergen.

Natürlich verlieren Sie die Möglichkeit, mithilfe undurchsichtiger Typen einfach zu steuern, wo genau die Struktur im Speicher zugeordnet ist, aber das ist nur ein bemerkenswerter Unterschied zwischen beispielsweise C und C++. C++ ist definitiv ein überlegenes Werkzeug, wenn es seine Fähigkeit vergleicht, objektorientierte Konzepte über C zu programmieren und gleichzeitig die Kontrolle über Speicherlayouts zu behalten. Dies bedeutet jedoch nicht unbedingt, dass Java oder C # C überlegen ist In dieser Hinsicht verlieren Sie völlig die Fähigkeit zu steuern, wo Objekte im Speicher zugeordnet sind, da diese beiden vollständig verlieren.

Und wir müssen eine Syntax wie fopen(file, ...); fclose(file); verwenden, im Gegensatz zu file.open(...); file.close();, aber big whoop. Wen interessiert das schon? Vielleicht nur jemand, der sich stark auf die automatische Vervollständigung in seiner IDE stützt. Ich gebe zu, dass dies aus praktischer Sicht eine sehr nützliche Funktion sein kann, aber vielleicht keine, die eine Diskussion darüber erfordert, ob eine Sprache für OOP geeignet ist.

Es fehlt uns die Fähigkeit, protected Felder effektiv zu implementieren. Ich werde mich dort total einreichen. Ich glaube jedoch nicht, dass es eine konkrete Regel gibt, die besagt: "Alle OO Sprachen sollten eine Funktion haben, mit der Unterklassen auf Mitglieder einer Basisklasse zugreifen können, auf die immer noch nicht zugegriffen werden sollte von normalen Kunden. "Außerdem sehe ich selten Anwendungsfälle für geschützte Mitglieder, die nicht zumindest ein bisschen misstrauisch sind, eine Wartungshürde zu werden.

Und natürlich müssen wir OO Polymorphismus mit Tabellen von Funktionszeigern und Zeigern auf diese für den dynamischen Versand mit etwas mehr Boilerplate emulieren, um diese analogen vtables und vptrs zu initialisieren, aber ein wenig von Boilerplate hat mir nie viel Kummer 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 downcasting wollen, und da würde ich sagen, wenn Sie downcasting überhaupt sein wollen, bitte verwenden Sie C nicht dafür, weil die Dinge, die Leute in C tun, um zu emulieren Downcasting können unter dem Gesichtspunkt der Typensicherheit schrecklich sein, aber ich möchte lieber, dass die Leute Downcasting überhaupt nicht. Typensicherheit ist etwas, das Sie in C leicht übersehen können, da der Compiler so viel Spielraum bietet, um Dinge nur als Bits und Bytes zu interpretieren, wodurch die Fähigkeit geopfert wird, potenzielle Fehler beim Kompilieren abzufangen, aber einige Sprachen, die als objektorientiert gelten, nicht nicht einmal statisch getippt.

Also keine Ahnung, ich denke es ist in Ordnung. Natürlich würde ich C nicht verwenden, um zu versuchen, eine große Codebasis zu erstellen, die den Prinzipien von SOLID] entspricht, aber dies liegt nicht unbedingt an den Mängeln in der objektorientierten Front. A. Viele der Funktionen, die ich vermissen würde, wenn ich versuchen würde, C für einen solchen Zweck zu verwenden, würden sich auf Sprachfunktionen beziehen, die nicht direkt als Voraussetzung für OOP wie starke Typensicherheit, Destruktoren, die automatisch aufgerufen werden) angesehen werden Wenn Objekte den Gültigkeitsbereich verlassen, Operatorüberladung, Vorlagen/Generika und Ausnahmebehandlung. Wenn mir die Zusatzfunktionen fehlen, die ich für C++ erreiche.

0
user204677

Schauen wir uns nur die Definition von OO an:

  • Messaging
  • Verkapselung
  • Späte Bindung

C liefert keine dieser drei. Insbesondere bietet es nicht Messaging, was das wichtigste ist.

0
Jörg W Mittag

Es gibt eine Reihe von Hauptbestandteilen von OO, aber die großen sind, dass der Großteil 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, tut dies auch sein Zustand), und wenn ein Code eine Operation für ein 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 der Mauer).

C macht die Einkapselung ganz gut; Code, der die Definition einer Struktur nicht sieht, kann (zu Recht) nicht hineinschauen. Alles, was Sie dazu tun müssen, ist, eine Definition wie diese in eine Header-Datei einzufügen:

struct FooImpl;
typedef struct FooImpl *Foo;

Natürlich wird eine Funktion benötigt, die Foos (dh eine Factory) erstellt und einen Teil der Arbeit an das zugewiesene Objekt selbst delegieren sollte (dh über eine "Konstruktor" -Methode), und auch eine Möglichkeit zu haben, 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 durch 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 as annehmen muss sein erstes Argument. Beim Versand geht es dann darum, die Funktion nachzuschlagen und mit dem richtigen Argument umzuschreiben, 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 erhalten Sie auch eine späte Bindung: Der Versandcode weiß nur, dass er einen bestimmten Offset in eine Tabelle aufruft, auf die das Objekt zeigt. Dies muss nur während der Zuordnung und Initialisierung des Objekts eingestellt werden. Es ist möglich, mit noch komplexeren Versandschemata zu arbeiten, die Ihnen mehr Laufzeitdynamik bieten (zu Lasten der Geschwindigkeit), aber sie sind Kirschen über dem Grundmechanismus.

Dies bedeutet jedoch nicht, dass C eine OO Sprache) ist. Der Schlüssel ist, dass C Ihnen die knifflige Arbeit überlässt, die Konventionen und den Versandmechanismus selbst zu schreiben (oder einen Drittanbieter zu verwenden) Bibliothek). Das ist eine Menge 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 gut beschrieben ist Durch ein OO Modell) wird eine OO Sprache) beim Schreiben der Lösung sehr nützlich sein. Die zusätzliche Komplexität kann gerechtfertigt werden.

0
Donal Fellows