Il linguaggio C, Programmazione ad oggetti

14 Giugno, 2009

Credete che per usare la programmazione ad oggetti si debba avere un linguaggio orientato alla programmazione ad oggetti? Sbagliato. Lo stile ad oggetti è ormai divenuto uno standard per la maggior parte dei linguaggi moderni come il Java ed il C++; non significa che questo stile non si possa implementare in linguaggi imperativi come il C.

In C, la struttura dati per rappresentare un "oggetto" è lo struct. Non si possono definire funzioni in uno struct (oltre a puntatori di funzioni), ma possiamo semplicemente creare funzioni che applichino qualche funzionamento sui dati dello struct stesso. In questo modo possiamo rappresentare la stessa metodologia di Classi e metodi usata in linguaggi OO, per esempio:

struct oca {
int vola;
};
struct oca *oca_new()
{
struct oca *o = malloc(sizeof(struct oca));
memset(o, 0xda, sizeof(struct oca));
return o;
}
void oca_falla_volare(struct oca *o)
{
o->vola = 1;
}
int main()
{
struct oca *oca = oca_new();
oca_falla_volare(oca);
}

Incapsulamento

Uno degli elementi fondamentali dell'OOP è l'incapsulazione. Possiamo usufruire del fatto che, in C, se implementiamo una struttura dati nel file ".c" invece che nell'apposito ".h", la struttura dati diventa invisibile al di fuori dello scope del file ".c". Allora come facciamo ad accedere allo struct al di fuori di esso? Semplice, tramite typedef.

// gatto.h
typedef struct gatto_t gatto;
// a questo punto il compilatore non sa nulla a riguardo dello struct,
// ma possiamo comunque definirci un tipo
gatto *gatto_new();
int gatto_set_corri(gatto *, int);

// gatto.c
#include "gatto.h"

struct gatto_t {
int corri;
};

gatto *gatto_new()
{
gatto *o = malloc(sizeof(gatto));
o->corri = 1; // possiamo dereferenzare il puntatore, perchè il compilatore
// riesce a vedere lo struct gatto_t, visto che è
// nello stesso scope
return 0;
}

int gatto_set_corri(gatto *o, int v)
{
o->corri = v;
}

// main.c
#include "gatto.h"

int main()
{
gatto *g = gatto_new();

int a = g->corri; // questo darà errore, non possiamo dereferenzare il
// membro dello struct visto che non ne abbiamo traccia
// nella definizione

int b = gatto_set_corri(g, 5); // questo invece funziona perchè
// la funzione è implementata nello
// stesso scope dello struct
}

Quando usare la programmazione ad oggetti col C

Negli ultimi anni il linguaggio C++ è cresciuto e maturato moltissimo, quindi è da buon programmatore chiedersi come e quando implementare questo stile ad oggetti con il linguaggio C. Sono riuscito ad identificare (in modo personale) alcuni condizioni che confermano la competitività dell'OOC (Object Oriented C):

L'uso indegno di questo stile nei miei programmi C mi ha aiutato moltissimo a creare applicazioni di alto livello, con API solide. La comprensione della programmazione ad oggetti che ho ottenuto è aumentata vertiginosamente, aiutandomi anche con altri linguaggi disegnati apposta.

© 2009 Pseudo Codice