Perché C non è considerato un linguaggio "orientato agli oggetti"?

91

Sembra che C abbia i suoi quasi-oggetti come 'structs' che possono essere considerati come oggetti (nel modo ad alto livello che normalmente penseremmo).

E inoltre, i file C stessi sono sostanzialmente "moduli" separati, giusto? Quindi i moduli non sono simili agli "oggetti"? Sono confuso sul motivo per cui C, che sembra così simile al C ++, è considerato un linguaggio "procedurale" di basso livello in cui, come C ++, è ad alto livello "orientato agli oggetti"

* modifica: (chiarimento) perché e dove, è la linea tracciata, per cosa è un 'oggetto' e non lo è?

    
posta Dark Templar 10.10.2011 - 22:44
fonte

12 risposte

101

It seems that C has its own quasi-objects such as 'structs' that can be considered as objects

Mettiamo insieme te e io attraverso la pagina di Wikipedia sulla programmazione orientata agli oggetti e controlla le funzionalità di C strutture in stile che corrispondono a quello che è tradizionalmente considerato uno stile orientato agli oggetti:

(OOP) is a programming paradigm using "objects" – data structures consisting of data fields and methods together with their interactions

Le strutture C consistono di campi e metodi insieme alle loro interazioni ? No.

Programming techniques may include features such as data abstraction, encapsulation, messaging, modularity, polymorphism, and inheritance.

Le strutture C fanno una di queste cose in un modo "di prima classe"? No. Il linguaggio funziona contro di te in ogni momento.

the object-oriented approach encourages the programmer to place data where it is not directly accessible by the rest of the program

Le strutture C lo fanno? No.

An object-oriented program will usually contain different types of objects, each type corresponding to a particular kind of complex data to be managed or perhaps to a real-world object or concept

Le strutture C lo fanno? Sì.

Objects can be thought of as wrapping their data within a set of functions designed to ensure that the data are used appropriately

No.

each object is capable of receiving messages, processing data, and sending messages to other objects

Può una struttura stessa inviare e ricevere messaggi? No. Può elaborare i dati? No.

OOP data structures tend to "carry their own operators around with them"

Questo succede in C? No.

Dynamic dispatch ... Encapsulation ... Subtype polymorphism ... Object inheritance ... Open recursion ... Classes of objects ... Instances of classes ... Methods which act on the attached objects ... Message passing ... Abstraction

Ci sono alcune di queste funzionalità delle strutture C? No.

Precisamente quali caratteristiche delle strutture pensi siano "orientate agli oggetti"? Perché non riesco a trovare qualsiasi se non il fatto che le strutture definiscono tipi .

Ora, ovviamente, puoi creare strutture con campi che puntano a funzioni. È possibile creare strutture con campi che puntano a matrici di puntatori di funzioni, corrispondenti a tabelle di metodi virtuali. E così via. Ovviamente emula C ++ in C. Ma questo è un modo molto non idiomatico di programmare in C; staresti meglio usando solo C ++.

And also, C files themselves are basically separate "modules", right? Then aren't modules kind of like 'objects' too?

Ancora una volta, a quali caratteristiche dei moduli stai pensando che li fanno agire come oggetti? I moduli supportano l'astrazione, l'incapsulamento, la messaggistica, la modularità, il polimorfismo e l'ereditarietà?

L'astrazione e l'incapsulamento sono piuttosto deboli. Ovviamente i moduli sono modulari; è per questo che si chiamano moduli. Messaging? Solo nel senso che una chiamata al metodo è un messaggio e i moduli possono contenere metodi. Polimorfismo? No. Eredità? No. I moduli sono candidati piuttosto deboli per "oggetti".

    
risposta data 10.10.2011 - 23:23
fonte
46

La parola chiave è "oriented", non "object". Anche il codice C ++ che usa oggetti ma li usa come strutture non è object oriented .

C e C ++ possono entrambi fare OOP (a parte nessun controllo di accesso in C), ma la sintassi per farlo in C è scomoda (per non dire altro), mentre la sintassi in C ++ lo rende molto invitante. C è orientato verso procedurale, mentre C ++ è orientato verso gli oggetti, nonostante le capacità core quasi identiche a tale riguardo.

Codice che usa oggetti per implementare progetti che possono essere fatti solo con oggetti (di solito significa sfruttare il polimorfismo) è un codice orientato agli oggetti. Il codice che usa gli oggetti come poco più di un sacco di dati, anche usando l'ereditarietà in un linguaggio orientato agli oggetti, è in realtà solo un codice procedurale che è più complicato di quanto dovrebbe essere. Il codice in C che utilizza i puntatori di funzione che vengono modificati in fase di esecuzione con le strutture piene di dati è un po 'come il polimorfismo, e si potrebbe dire che è "orientato agli oggetti", anche in un linguaggio orientato alla procedura.

    
risposta data 10.10.2011 - 23:19
fonte
19

Basato sui principi di livello più alto:

Un oggetto è un incapsulamento di dati e comportamenti in un modo interconnesso in modo che operino nel loro insieme che può essere istanziato più volte e lavorato come una scatola nera se si conosce l'interfaccia esterna.

Le strutture contengono dati ma nessun comportamento e quindi non possono essere considerati oggetti.

I moduli contengono sia il comportamento che i dati ma non sono incapsulati in modo tale che i due siano correlati e certamente non possono essere istanziati più volte.

E questo prima di entrare nell'ereditarietà e nel polimorfismo ...

    
risposta data 10.10.2011 - 22:58
fonte
6

"structs" sono solo dati. Il solito test rapido e sporco di "orientamento dell'oggetto" è: "Esiste una struttura che consente di incapsulare codice e dati come una singola unità?". C fallisce, ed è quindi procedurale. Il C ++ supera questo test.

    
risposta data 10.10.2011 - 22:47
fonte
5

C, proprio come C ++, ha la capacità di fornire Data Abstraction , che è uno idioma del paradigma di programmazione orientata agli oggetti che esisteva prima di esso.

  • Le strutture C possono contenere dati (e questo è il loro scopo principale)
  • Le strutture C possono anche definire i puntatori di funzione come dati
  • Le strutture C possono e spesso hanno un insieme di funzioni ad esse associate, proprio come i metodi, solo il puntatore questo non viene passato implicitamente, ma è necessario specificarlo esplicitamente come primo argomento per ogni metodo progettato per gestire la struttura specificata. C ++ lo fa automaticamente per te, sia quando definisci sia chiama metodi class / struct.

OOP in C ++ estende i mezzi per astrarre i dati. Alcuni dicono che è dannoso , mentre altri lo considerano un buon strumento quando usato correttamente.

  • C ++ rende implicito il puntatore questo non richiedendo all'utente di passarlo in "metodi della classe / struct" a condizione che il tipo possa essere (almeno in parte) identificato.
  • C ++ consente di limitare l'accesso a determinati metodi (funzioni di classe), e quindi consente una maggiore "programmazione difensiva" o "prova dell'idiota".
  • C ++ incoraggia le astrazioni fornendo una maggiore sicurezza del tipo introducendo
    1. Il nuovo operatore invece di malloc + cast
    2. Modelli invece dei puntatori void
    3. Funzioni inline che ricevono valori digitati invece di macro
    4. Costruito in Polymorphism che non devi implementare tu stesso, che ti consente di creare specializzazioni gerarchie di astrazione , contratti e .

Tuttavia, troverai molti "hacker" C che predicano come C è perfettamente in grado di ottenere la giusta quantità di astrazione e come l'overhead creato da C ++ li distrae solo dalla risoluzione del problema reale.

Inefficient abstracted programming models where two years down the road you notice that some abstraction wasn't very efficient, but now all your code depends on all the nice object models around it, and you cannot fix it without rewriting your app. -- Linus Torvalds

Gli altri tendono a guardarlo in modo più equilibrato, accettando sia i vantaggi che gli svantaggi.

C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off. -- Bjarne Stroustrup

    
risposta data 11.10.2011 - 01:51
fonte
2

Devi guardare l'altro lato della medaglia: C ++.

In OOP, pensiamo a un oggetto astratto (e progettiamo il programma di conseguenza), ad esempio un'auto che può fermarsi, accelerare, girare a sinistra oa destra, ecc. Una struttura con un fascio di funzioni semplicemente non si adatta al concetto .

Con oggetti "reali" abbiamo bisogno di nascondere i membri, per esempio, o possiamo anche avere ereditarietà con una vera relazione "è una" e molti altri.

DOPO LA LETTURA DEI COMMENTI QUI SOTTO: Beh, è giusto che (quasi) tutto si possa fare con C (che è sempre vero), ma a prima vista ho pensato che ciò che separa c da c ++ è il modo in cui pensi quando progetti un programma.

L'unica cosa che fa davvero la differenza è l'imposizione di politiche da parte del compilatore . la pura funzione virtuale, e così via. ma questa risposta riguarderà solo problemi tecnici, ma penso che la differenza principale (come detto) sia il modo originale che si pensa mentre si codifica, dal momento che C ++ offre una sintassi incorporata migliore per fare queste cose, invece di fare OOP in un modo un po 'maldestro in C.

    
risposta data 10.10.2011 - 22:52
fonte
1

Lo hai detto tu stesso. Sebbene C abbia cose simili agli oggetti, non sono ancora oggetti, ed è per questo che C non è considerato un linguaggio OOP.

    
risposta data 10.10.2011 - 22:49
fonte
1

Orientato agli oggetti si riferisce sia a un modello architettonico (o anche a un meta-modello), e, le lingue che hanno caratteristiche per aiutare a implementare o mandare usando questo modello.

È possibile implementare un design "OO" (il desktop Gnome è forse il miglior esempio di OO fatto in pura C), l'ho già visto fatto con COBOL!

Tuttavia, essere in grado di implementare una dose di progettazione OO non rende il linguaggio OO. I puristi sostengono che Java e C ++ non sono veramente OO poiché non è possibile sovrascrivere o ereditare i "tipi" di base come "int" e "char" e, Java non supporta l'ereditarietà multipla. Ma poiché sono i linguaggi OO più utilizzati e supportano la maggior parte del paradigma, i programmatori più "veri" che vengono pagati per produrre codice funzionante li considerano come le lingue OO.

C d'altra parte supporta solo le strutture (come COBOL, Pascal e dozzine di altri linguaggi procedurali), si potrebbe sostenere che supporta l'ereditarietà multipla in quanto è possibile utilizzare qualsiasi funzione su qualsiasi pezzo di dati, ma la maggior parte lo considererebbe come un bug piuttosto che una funzionalità.

    
risposta data 11.10.2011 - 03:56
fonte
0

Diamo un'occhiata alla definizione di OO:

  • Messaggi
  • incapsulamento
  • Rilegatura ritardata

C non fornisce nessuno di questi tre. In particolare, non fornisce Messaggi , che è il più importante.

    
risposta data 11.10.2011 - 04:00
fonte
0

Ci sono un certo numero di ingredienti chiave per OO, ma i più grandi sono che la maggior parte del codice non sa cosa c'è dentro un oggetto (vedono l'interfaccia di superficie, non l'implementazione), che lo stato di un oggetto è un'unità gestita (cioè, quando l'oggetto cessa di essere, così fa il suo stato), e quando qualche codice richiama un'operazione su un oggetto, lo fa senza sapere esattamente che cosa sia o cosa implichi quell'operazione (tutto ciò che fanno è seguire uno schema lanciare un "messaggio" sul muro).

C fa incapsulamento bene; codice che non vede la definizione di una struttura non può (legittimamente) sbirciare al suo interno. Tutto quello che devi fare è mettere una definizione come questa in un file di intestazione:

struct FooImpl;
typedef struct FooImpl *Foo;

Naturalmente, ci sarà bisogno di una funzione che costruisca Foo s (cioè una factory) e che dovrebbe delegare parte del lavoro all'oggetto assegnato stesso (cioè, attraverso un metodo "costruttore"), e anche avere un modo di smaltire nuovamente l'oggetto (pur lasciandolo ripulire tramite il suo metodo "distruttore") ma questo è dettagli.

L'invio del metodo (es. messaggistica) può essere fatto anche attraverso la convenzione che il primo elemento della struttura è in realtà un puntatore a una struttura piena di puntatori di funzione e che ognuno di quei puntatori di funzione deve prendere un Foo come il suo primo argomento. L'invio diventa quindi una questione di cercare la funzione e chiamarla con la giusta riscrittura dell'argomento, il che non è così difficile da fare con un macro e un po 'di astuzia. (Quella tabella di funzioni è il nucleo di ciò che una classe è realmente in un linguaggio come C ++.)

Inoltre, questo ti dà anche il binding in ritardo: tutto il codice di invio sa che sta invocando un particolare offset in una tabella puntata dall'oggetto. Deve essere impostato solo durante l'allocazione e l'inizializzazione dell'oggetto. È possibile utilizzare schemi di spedizione ancora più complessi che ti consentono di aumentare il dinamismo di runtime (a un costo di velocità), ma sono ciliegie al di sopra del meccanismo di base.

Tuttavia ciò non significa che C sia un linguaggio OO. La chiave è che C ti lascia fare tutto il difficile lavoro di scrivere te stesso le convenzioni e il meccanismo di invio (o usare una libreria di terze parti). È un sacco di lavoro. Inoltre non fornisce supporto sintattico o semantico, quindi implementare un sistema completo di classe (con cose come l'ereditarietà) sarebbe inutilmente doloroso; se hai a che fare con un problema complesso che è ben descritto da un modello OO, un linguaggio OO sarà molto utile per scrivere la soluzione. La complessità extra può essere giustificata.

    
risposta data 11.10.2011 - 10:27
fonte
0

Penso che C sia perfetto e discreto per implementare concetti orientati agli oggetti, scrollare le spalle . La maggior parte delle differenze, per come la vedo tra il sottotitolo di denominatore comune delle lingue considerate orientate agli oggetti, sono di natura minore e sintattica dal mio punto di vista pragmatico.

Iniziamo con, diciamo, nascondere le informazioni. In C possiamo ottenere ciò semplicemente nascondendo la definizione di una struttura e lavorando con essa attraverso puntatori opachi. In questo modo si modella efficacemente la distinzione public rispetto a private dei campi dati come si ottiene con le classi. Ed è abbastanza facile da fare e difficilmente anti-idiomatico, dato che la libreria C standard si basa su questo per ottenere informazioni nascoste.

Ovviamente si perde la capacità di controllare facilmente dove viene allocata la struttura in memoria usando tipi opachi, ma questa è solo una differenza degna di nota tra, ad esempio, C e C ++. C ++ è sicuramente uno strumento superiore quando si confronta la sua capacità di programmare concetti orientati agli oggetti su C pur mantenendo il controllo sui layout di memoria, ma ciò non significa necessariamente che Java o C # siano superiori a C in questo senso, dal momento che questi due ti rendono perdi completamente la capacità di controllare dove gli oggetti sono allocati in memoria.

E dobbiamo usare una sintassi come fopen(file, ...); fclose(file); invece di file.open(...); file.close(); ma grande whoop. A chi importa davvero? Forse solo qualcuno che si appoggia pesantemente al completamento automatico nel proprio IDE. Ammetto che può essere una caratteristica molto utile dal punto di vista pratico, ma forse non è quella che richiede una discussione sul fatto che una lingua sia adatta per OOP.

Non abbiamo la possibilità di implementare efficacemente i campi protected . Ci arriverò totalmente lì. Ma non credo che ci sia una regola concreta che dice " Tutti i linguaggi OO dovrebbero avere una funzionalità che permetta alle sottoclassi di accedere ai membri di una classe base che non dovrebbero ancora essere accessibili dai normali client ." Inoltre raramente vedo casi d'uso per membri protetti che non sono almeno un po 'sospettosi di diventare un ostacolo alla manutenzione.

E ovviamente dobbiamo "emulare" il polimorfismo OO con tabelle di puntatori di funzione e puntatori a loro per l'invio dinamico con un po 'più di piastra per inizializzare quelli analogici vtables e vptrs , ma un po' di piastra di fondo mai mi ha causato molto dolore.

L'ereditarietà è più o meno allo stesso modo. Possiamo facilmente modellarlo attraverso la composizione e al funzionamento interno dei compilatori si riduce alla stessa cosa. Ovviamente perdiamo la sicurezza del tipo se vogliamo downcast , e lì direi se vuoi essere downcasting , per favore non usare C per questo perché il le cose che le persone fanno in C per emulare downcasting possono essere orribili da un punto di vista sulla sicurezza, ma preferirei che le persone non emettessero downcast . La sicurezza di tipo è qualcosa che si può facilmente iniziare a perdere in C poiché il compilatore offre un margine di manovra tale da interpretare le cose solo come bit e byte, sacrificando la possibilità di individuare gli errori potenziali in fase di compilazione, ma alcuni linguaggi considerati aren orientati agli oggetti 't anche staticamente digitato.

Quindi non so, penso che vada bene. Ovviamente non userei C per provare a creare un codebase su larga scala che sia conforme ai principi SOLID, ma non necessariamente a causa delle sue mancanze nel fronte orientato agli oggetti. Molte delle funzionalità che mi mancherebbero se tentassi di usare C per un tale scopo sarebbero correlate a caratteristiche del linguaggio non direttamente considerate un prerequisito per OOP come sicurezza di tipo strong, distruttori che vengono automaticamente richiamati quando gli oggetti escono dall'oscilloscopio, operatore sovraccarico, modelli / generici e gestione delle eccezioni. È quando mi mancano quelle funzioni ausiliarie che raggiungo per C ++.

    
risposta data 19.12.2017 - 01:33
fonte
-1

Non una brutta domanda.

Se hai intenzione di chiamare c una lingua OO, dovresti chiamare praticamente tutti i linguaggi procedurali OO. Quindi renderebbe il termine senza significato. c non ha supporto linguistico per OO. Se ha structs, ma structs is types no classes.

In realtà c non ha molte funzionalità rispetto alla maggior parte delle lingue. È principalmente utilizzato per la sua velocità, semplicità, popolarità e supporto, incluse tonnellate di librerie.

    
risposta data 11.10.2011 - 10:06
fonte

Leggi altre domande sui tag