Parliamo della soluzione ingegneristica completa che è stata considerata la migliore pratica in passato.
Il problema con le strutture è che tutto è pubblico, quindi non ci sono dati nascosti.
Possiamo aggiustarlo.
Si creano due file di intestazione. Uno è il file di intestazione "pubblico" utilizzato dai client del tuo codice. Esso contiene le definizioni in questo modo:
typedef struct t_ProcessStruct *t_ProcessHandle;
extern t_ProcessHandle NewProcess();
extern void DisposeProcess(t_ProcessHandle handle);
typedef struct t_PermissionsStruct *t_PermissionsHandle;
extern t_PermissionsHandle NewPermissions();
extern void DisposePermissions(t_PermissionsHandle handle);
extern void SetProcessPermissions(t_ProcessHandle proc, t_PermissionsHandle perm);
quindi si crea un file di intestazione privato che contiene le definizioni in questo modo:
typedef void (*fDisposeFunction)(void *memoryBlock);
typedef struct {
fDisposeFunction _dispose;
} t_DisposableStruct;
typedef struct {
t_DisposableStruct_disposer; /* must be first */
PID _pid;
/* etc */
} t_ProcessStruct;
typedef struct {
t_DisposableStruct_disposer; /* must be first */
PERM_FLAGS _flags;
/* etc */
} t_PermissionsStruct;
e poi nell'implementazione si può fare qualcosa di simile:
static void DisposeMallocBlock(void *process) { if (process) free(process); }
static void *NewMallocedDisposer(size_t size)
{
assert(size > sizeof(t_DisposableStruct);
t_DisposableStruct *disp = (t_DisposableStruct *)malloc(size);
if (disp) {
disp->_dispose = DisposeMallocBlock;
}
return disp;
}
static void DisposeUsingDisposer(t_DisposableStruct *ds)
{
assert(ds);
ds->_dispose(ds);
}
t_ProcessHandle NewProcess()
{
t_ProcessHandle proc = (t_ProcessHandle)NewMallocedDisposer(sizeof(t_ProcessStruct));
if (proc) {
proc->PID = NextPID(); /* etc */
}
return proc;
}
void DisposeProcess(t_ProcessHandle proc)
{
DisposeUsingDisposer(&(proc->_disposer));
}
Quello che succede è che tu fai dichiarazioni in avanti per le tue strutture nei tuoi file di intestazione pubblici. Ora le tue strutture sono opache, il che significa che i clienti non possono dick con loro. Quindi, nella dichiarazione completa, includi un distruttore all'inizio di ogni struttura che puoi chiamare genericamente. È possibile utilizzare lo stesso allocatore malloc per tutti la stessa funzione di smaltimento e così via. Rendi pubbliche le funzioni set/get per gli elementi che desideri vengano esposti.
Improvvisamente, il tuo codice è molto più sano. È possibile ottenere solo le strutture dagli allocatori o la funzione che chiama allocatori, il che significa che è possibile bloccare l'inizializzazione. Costruisci in distruttori in modo che l'oggetto possa essere distrutto. E su te vai. A proposito, un nome migliore di t_DisposableStruct potrebbe essere t_vTableStruct, perché è quello che è. Ora puoi costruire l'ereditarietà virtuale avendo un vTableStruct che è tutti i puntatori di funzione. Puoi anche fare cose che non puoi fare in un linguaggio puro (in genere), come cambiare elementi selezionati del vtable al volo.
Il punto importante è che lo è un modello di progettazione per rendere le strutture sicure e inizializzabili.
Oh cfront, come ci manchi. – joeforker
... come un'orribile eruzione cutanea. – Randolpho
E in che modo Cfront gestisce il costruttore all'inizio? – claf