Leggi SICP e apprendi Schema e l' idea pratica di tipi di dati astratti . Quindi la codifica in C è facile (poiché con SICP, un po 'di C e un po' di PHP, Ruby, ecc ... il tuo pensiero sarebbe abbastanza ampio e capiresti che la programmazione orientata agli oggetti potrebbe non essere lo stile migliore in tutti i casi, ma solo per alcuni tipi di programmi). Fai attenzione all'allocazione dinamica della memoria C , che è probabilmente la parte più difficile. Lo standard del linguaggio di programmazione C99 o C11 e la sua libreria standard C sono in realtà piuttosto scadenti (non conoscono TCP o directory!) E spesso avrai bisogno di alcune librerie o interfacce esterne (ad es.POSIX , libcurl per libreria client HTTP, libonion per libreria server HTTP, GMPlib per bignum, alcune librerie come libunistring per UTF-8, ecc ...).
I tuoi "oggetti" sono spesso in C alcuni-correlati struct
e tu definisci l'insieme di funzioni che operano su di essi. Per funzioni brevi o molto semplici, considera di definirle, con i relativi struct
, come static inline
in alcuni file di intestazione che foo.h
devono essere #include
-d altrove.
Si noti che la programmazione orientata agli oggetti non è l'unico paradigma di programmazione . In alcune occasioni, sono utili altri paradigmi ( programmazione funzionale alla Ocaml o Haskell o persino Scheme o Commmon Lisp, programmazione logica alla Prolog, ecc. Ecc . Leggi anche il blog di J.Pitrat sull'intelligenza artificiale dichiarativa). Vedi il libro di Scott: Programming Language Pragmatics
In realtà, un programmatore in C, o in Ocaml, di solito non vuole programmare in uno stile di programmazione orientato agli oggetti. Non c'è motivo di forzarti a pensare agli oggetti quando ciò non è utile.
struct
Ne definirai alcune e le funzioni che operano su di esse (spesso tramite puntatori). Potresti aver bisogno di alcuni sindacati con tag (spesso, uno struct
con un membro tag, spesso alcuni enum
e alcuni union
all'interno), e potresti trovare utile avere un membro dell'array flessibile alla fine di alcuni dei tuoi struct
-s.
Guarda all'interno del codice sorgente di alcuni software gratuiti esistenti in C (vedi github e sourceforge
per trovarne alcuni). Probabilmente, installare e utilizzare una distribuzione Linux sarebbe utile: è fatto quasi solo di software libero, ha grandi compilatori C di software libero ( GCC , Clang / LLVM ) e strumenti di sviluppo. Vedi anche Advanced Linux Programming se vuoi sviluppare per Linux.
Non dimenticare di compilare tutti gli avvisi e le informazioni di debug, ad esempio, in gcc -Wall -Wextra -g
particolare durante le fasi di sviluppo e debug, e imparare ad usare alcuni strumenti, ad esempio valgrind per cacciare perdite di memoria , gdb
debugger, ecc. Fai attenzione a capire bene cosa non è definito comportamento ed evitarlo fortemente (ricorda che un programma potrebbe avere un certo UB e talvolta sembra "funzionare").
Quando hai davvero bisogno di costrutti orientati agli oggetti (in particolare ereditarietà ) puoi usare i puntatori alle strutture correlate e alle funzioni. Potresti avere il tuo macchinario vtable , ogni "oggetto" che inizia con un puntatore a puntatori di una struct
funzione contenente. Sfruttate la possibilità di trasmettere un tipo di puntatore a un altro tipo di puntatore (e del fatto che è possibile eseguire il cast da un struct super_st
contenente gli stessi tipi di campo di quelli che iniziano struct sub_st
a emulare l'ereditarietà). Si noti che C è sufficiente per implementare sistemi di oggetti piuttosto sofisticati, in particolare seguendo alcune convenzioni , come dimostra GObject (da GTK / Gnome).
Quando hai davvero bisogno di chiusure , le emulerai spesso con i callback , con la convenzione che a ogni funzione che utilizza un callback vengono passati sia un puntatore a funzione che alcuni dati client (consumati dal puntatore a funzione quando lo chiama). Potresti anche avere (convenzionalmente) il tuo tipo di chiusura struct
(contenente un puntatore a funzione e i valori chiusi).
Poiché C è un linguaggio di livello molto basso, è importante definire e documentare le proprie convenzioni (ispirate alla pratica in altri programmi C), in particolare sulla gestione della memoria, e probabilmente anche su alcune convenzioni di denominazione. È utile avere qualche idea sull'architettura del set di istruzioni . Non dimenticare che un compilatore C può fare molte ottimizzazioni sul tuo codice (se lo chiedi), quindi non preoccuparti troppo di fare le micro-ottimizzazioni a mano, lascialo al tuo compilatore ( gcc -Wall -O2
per una compilazione ottimizzata di rilasciati Software). Se ti interessa il benchmarking e le prestazioni non elaborate, dovresti abilitare le ottimizzazioni (una volta eseguito il debug del programma).
Non dimenticare che a volte la metaprogrammazione è utile . Molto spesso, i software di grandi dimensioni scritti in C contengono alcuni script o programmi ad-hoc per generare del codice C usato altrove (e potresti anche giocare a trucchi del preprocessore C sporchi , ad esempio X-macro ). Esistono alcuni generatori di programmi C utili (ad es. Yacc o gnu bison per generare parser, gperf per generare funzioni hash perfette, ecc ...). Su alcuni sistemi (in particolare Linux e POSIX) potresti persino generare del codice C in fase di runtime nel generated-001.c
file, compilarlo in un oggetto condiviso eseguendo alcuni comandi (come gcc -O -Wall -shared -fPIC generated-001.c -o generated-001.so
) in fase di runtime, caricare dinamicamente l'oggetto condiviso utilizzando dlopene ottieni un puntatore a funzione da un nome usando dlsym . Sto facendo questi trucchi in MELT (un linguaggio specifico del dominio simile a Lisp che potrebbe esserti utile, poiché consente la personalizzazione del compilatore GCC ).
Fai attenzione ai concetti e alle tecniche di garbage collection (il conteggio dei riferimenti è spesso una tecnica per gestire la memoria in C, ed è IMHO una cattiva forma di garbage collection che non si occupa bene dei riferimenti circolari ; potresti avere dei punti deboli per aiutarti, ma potrebbe essere complicato). In alcune occasioni, potresti prendere in considerazione l'uso del garbage collector conservatore di Boehm .
qux = foo.bar(baz)
diventanoqux = Foo_bar(foo, baz)
.