Che cos'è l'incapsulamento in fase di compilazione in C?


9

Durante la ricerca dei vantaggi di C rispetto a C ++, mi sono imbattuto in questo paragrafo:

Il modo standard in C per eseguire l'incapsulamento è inoltrare una struttura e consentire l'accesso ai suoi dati solo attraverso le funzioni. Questo metodo crea anche l'incapsulamento del tempo di compilazione. L'incapsulamento in fase di compilazione ci consente di modificare i membri delle strutture dati senza ricompilare il codice client (altro codice utilizzando la nostra interfaccia). D'altro canto, il modo standard di eseguire l'incapsulamento C ++ (utilizzando le classi) richiede la ricompilazione del codice client quando si aggiungono o rimuovono variabili di membri privati.

Capisco in che modo dichiarare una struttura e accedere ai suoi membri attraverso le funzioni nasconde i dettagli di implementazione della struttura. Quello che non capisco è questa linea in particolare:

L'incapsulamento del tempo di compilazione ci consente di modificare i membri delle strutture dati senza ricompilare il codice client (altro codice utilizzando la nostra interfaccia).

In quale scenario è applicabile?


Fondamentalmente, structè una scatola nera con interni sconosciuti. Se il client non conosce gli interni, non può mai accedervi direttamente e puoi cambiarli a piacimento. Questo è simile all'incapsulamento in OOP. Gli interni sono privati ​​e si modifica l'oggetto solo con metodi pubblici.
Sulthan,

Questo non è sempre vero. Se decidi di aggiungere / rimuovere membri di una struttura, ne modifichi le dimensioni. Ciò richiederà la ricompilazione del codice client.
DarkAtom,

2
@DarkAtom Non è vero! Se il client non conosce i contenuti (una struttura opaca ), non conosce le sue dimensioni, quindi la modifica delle dimensioni non è un problema.
Adrian Mole,

1
@DarkAtom: consentire l'accesso a una struttura solo tramite funzioni include l'allocazione solo tramite funzioni. La libreria fornirebbe una funzione per allocare una struttura e il client non ne conoscerebbe mai le dimensioni. La modifica delle dimensioni non richiede la ricompilazione del client.
Eric Postpischil,

3
Si noti che questo non è tecnicamente un "vantaggio di C su C ++" in quanto è possibile (e spesso si fa) implementare la stessa idea in C ++. Cerca il linguaggio "brufolo" .
user4815162342

Risposte:


4

Un possibile scenario del mondo reale in cui ciò potrebbe verificarsi è quando una libreria di database, scritta nei giorni in cui lo spazio su disco rigido era molto limitato, utilizzava un singolo byte per memorizzare il campo "anno" di una data (ad esempio 11-NOV-1973 avrebbe 73per l'anno). Ma, quando è arrivato il 2000, questo non sarebbe più stato sufficiente e l'anno doveva essere archiviato come un intero breve (16 bit). L'intestazione pertinente (molto semplificata) per questa libreria potrebbe essere questa:

// dbEntry.h
typedef struct _dbEntry dbEntry;

dbEntry* CreateDBE(int day, int month, int year, int otherData);
void DeleteDBE(dbEntry* entry);
int GetYear(dbEntry* entry);

E un programma "client" sarebbe:

#include <stdio.h>
#include "dbEntry.h"

int main()
{
    int dataBlob = 42;
    dbEntry* test = CreateDBE(17, 11, 2019, dataBlob);
    //...
    int year = GetYear(test);
    printf("Year = %d\n", year);
    //...
    DeleteDBE(test);
    return 0;
}

L'implementazione "originale":

#include <stdlib.h>
#include "dbEntry.h"

struct _dbEntry {
    unsigned char d;
    unsigned char m;
    unsigned char y;    // Fails at Y2K!
    int dummyData;
};

dbEntry* CreateDBE(int day, int month, int year, int otherData)
{
    dbEntry* local = malloc(sizeof(dbEntry));
    local->d = (unsigned char)(day);
    local->m = (unsigned char)(month);
    local->y = (unsigned char)(year % 100);
    local->dummyData = otherData;
    return local;
}

void DeleteDBE(dbEntry* entry)
{
    free(entry);
}

int GetYear(dbEntry* entry)
{
    return (int)(entry->y);
}

Quindi, all'avvicinarsi di Y2K, questo file di implementazione verrebbe modificato come segue (tutto il resto non verrebbe toccato):

struct _dbEntry {
    unsigned char d;
    unsigned char m;
    unsigned short y;   // Can now differentiate 1969 from 2069
    int dummyData;
};

dbEntry* CreateDBE(int day, int month, int year, int otherData)
{
    dbEntry* local = malloc(sizeof(dbEntry));
    local->d = (unsigned char)(day);
    local->m = (unsigned char)(month);
    local->y = (unsigned short)(year);
    local->dummyData = otherData;
    return local;
}

Quando il client deve essere aggiornato per utilizzare la nuova versione (sicura per Y2K), non sono necessarie modifiche al codice. In realtà, si potrebbe anche non avere ricompilare: semplicemente ri-linking alla libreria di oggetti aggiornata (se è questo che si tratta) potrebbe essere sufficiente.


2

Nota: il seguente elenco non sarà esaustivo. Le modifiche sono benvenute!

Gli scenari applicabili includono:

  • Applicazioni multi-modulo in cui non si desidera la ricompilazione per qualche motivo.
  • Strutture utilizzate nelle librerie in cui non si desidera forzare la ricompilazione degli utenti della libreria ogni volta che si modifica una struttura (pubblicata).
  • Strutture che contengono elementi diversi sulle diverse piattaforme su cui lavora il modulo.

La struttura più conosciuta di questo tipo è FILE. Basta chiamare fopen()e ottenere un puntatore in caso di successo. Questo puntatore viene quindi passato a ogni altra funzione che funziona sui file. Ma non sai - e non vuoi sapere - i dettagli, come gli elementi contenuti e le dimensioni.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.