¿Qué es la encapsulación en tiempo de compilación en C?

9

Cuando estaba investigando las ventajas de C sobre C ++, me encontré con este párrafo:

La forma estándar en C de encapsular es declarar hacia adelante una estructura y solo permitir el acceso a sus datos a través de funciones. Este método también crea la encapsulación en tiempo de compilación. La encapsulación en tiempo de compilación nos permite cambiar los miembros de las estructuras de datos sin recompilar el código del cliente (otro código utilizando nuestra interfaz). La forma estándar de encapsular C ++ por otro lado (usando clases) requiere la recompilación del código del cliente al agregar o eliminar variables de miembros privados.

Entiendo cómo declarar hacia adelante una estructura y acceder a sus miembros a través de funciones oculta los detalles de implementación de la estructura. Lo que no entiendo es esta línea específicamente:

La encapsulación en tiempo de compilación nos permite cambiar los miembros de las estructuras de datos sin recompilar el código del cliente (otro código utilizando nuestra interfaz).

¿En qué escenario es esto aplicable?

mbl
fuente
Básicamente, structes una caja negra con elementos internos desconocidos. Si el cliente no conoce las partes internas, nunca podrá acceder a ellas directamente y puede cambiarlas a voluntad. Esto es similar a la encapsulación en OOP. Los elementos internos son privados y solo cambia el objeto utilizando métodos públicos.
Sulthan
Esto no siempre es cierto. Si decide agregar / eliminar miembros de una estructura, cambia su tamaño. Esto requerirá la recompilación del código del cliente.
DarkAtom
2
@DarkAtom ¡No es cierto! Si el cliente no conoce el contenido (una estructura opaca ), entonces no conoce su tamaño, por lo que cambiar el tamaño no es un problema.
Adrian Mole
1
@DarkAtom: permitir el acceso a una estructura solo a través de funciones incluye la asignación solo a través de funciones. La biblioteca proporcionaría una función para asignar una estructura, y el cliente nunca conocería su tamaño. Cambiar el tamaño no requiere volver a compilar el cliente.
Eric Postpischil
3
Tenga en cuenta que esto técnicamente no es una "ventaja de C sobre C ++", ya que puede (y a menudo lo hace) implementar la misma idea en C ++. Busque el idioma "pimpl" .
user4815162342

Respuestas:

4

Un posible escenario del mundo real en el que esto ocurriría es cuando una biblioteca de base de datos, escrita en los días en que el espacio en el disco duro era muy limitado, usaba un solo byte para almacenar el campo 'año' de una fecha (por ejemplo, 11-NOV-1973 tendría 73para el año). Pero, cuando llegó el año 2000, esto ya no sería suficiente, y el año tuvo que ser almacenado como un entero corto (16 bits). El encabezado relevante (muy simplificado) para esta biblioteca podría ser este:

// dbEntry.h
typedef struct _dbEntry dbEntry;

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

Y un programa 'cliente' sería:

#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;
}

La implementación 'original':

#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);
}

Luego, en el enfoque de Y2K, este archivo de implementación se cambiaría de la siguiente manera (todo lo demás no se modifica):

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;
}

Cuando el cliente necesita ser actualizado para usar la nueva versión (segura para Y2K), no se requerirán cambios de código. De hecho, es posible que ni siquiera tenga que volver a compilar: simplemente volver a vincular a la biblioteca de objetos actualizada (si eso es lo que es) podría ser suficiente.

Adrian Mole
fuente
2

Nota: La siguiente lista no será exhaustiva. ¡Las ediciones son bienvenidas!

Los escenarios aplicables incluyen:

  • Aplicaciones de módulos múltiples donde no desea recompilación por alguna razón.
  • Estructuras utilizadas en bibliotecas donde no desea obligar a los usuarios de la biblioteca a que se vuelvan a compilar cada vez que cambie una estructura (publicada).
  • Estructuras que contienen diferentes elementos en las diferentes plataformas en las que trabaja el módulo.

La estructura más conocida de este tipo es FILE. Simplemente llame fopen()y obtenga un puntero si tiene éxito. Este puntero se entrega entre sí a la función que funciona en los archivos. Pero no sabe, y no quiere saber, los detalles, como los elementos contenidos y el tamaño.

la abeja ocupada
fuente