Що таке інкапсуляція часу компіляції в C?


9

Коли я досліджував переваги C над C ++, я натрапив на цей абзац:

Стандартний спосіб для інкапсуляції на C - це пересилання оголошень структури та надання доступу до її даних лише через функції. Цей метод також створює інкапсуляцію часу компіляції. Час інкапсуляції компіляції дозволяє нам змінювати члени структур даних без перекомпіляції клієнтського коду (інший код за допомогою нашого інтерфейсу). З іншого боку, стандартний спосіб робити інкапсуляцію C ++ (використовуючи класи) вимагає перекомпіляції клієнтського коду при додаванні чи видаленні змінних приватних членів.

Я розумію, як вперед декларувати структуру та отримати доступ до її членів за допомогою функцій приховує деталі реалізації структури. Що я не розумію, це конкретно ця лінія:

Час інкапсуляції компіляції дозволяє нам змінювати члени структур даних без перекомпіляції клієнтського коду (інший код за допомогою нашого інтерфейсу).

У якому сценарії це застосовно?


В основному, structце чорний ящик з невідомими внутрішніми. Якщо клієнт не знає внутрішніх справ, він ніколи не може отримати доступ до них безпосередньо, і ви можете змінити їх за бажанням. Це схоже на інкапсуляцію в ООП. Внутрішні внутрішні дані є приватними, і ви змінюєте об'єкт лише публічними методами.
Султан

Це не завжди так. Якщо ви вирішили додати / видалити члени структури, ви зміните її розмір. Для цього знадобиться перекомпіляція коду клієнта.
DarkAtom

2
@DarkAtom Неправда! Якщо клієнт не знає вмісту ( непрозора структура), він не знає його розміру, тому зміна розміру не є проблемою.
Адріан Мол

1
@DarkAtom: Дозволення доступу до структури лише за допомогою функцій включає розподіл лише через функції. Бібліотека забезпечувала б функцію розподілу структури, і клієнт ніколи не дізнається її розмір. Зміна розміру не потребує перекомпіляції клієнта.
Eric Postpischil

3
Зауважте, що це технічно не є «перевагою C над C ++», оскільки ви можете (і часто це робите) реалізувати ту саму ідею в C ++. Подивіться на ідіому "pimpl" .
user4815162342

Відповіді:


4

Можливий сценарій у реальному світі, коли це відбудеться, коли бібліотека баз даних, написана в дні, коли місце на жорсткому диску було дуже обмеженим, використовувала один байт для зберігання поля 'рік' дати (наприклад, 11-НОВ-1973 мав би 73за рік). Але коли настав 2000 рік, цього вже не буде достатньо, і рік тоді повинен бути збережений як коротке (16-бітове) ціле число. Відповідним (значно спрощеним) заголовком для цієї бібліотеки може бути таке:

// dbEntry.h
typedef struct _dbEntry dbEntry;

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

А програма «клієнт» була б:

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

Оригінальна реалізація:

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

Тоді, при наближенні Y2K, цей файл реалізації буде змінено наступним чином (все інше залишилося недоторканим):

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

Коли клієнт потребує оновлення, щоб використовувати нову (безпечну для Y2K) версію, ніяких змін коду не потрібно. Насправді, ви можете навіть не доведеться перекомпілювати: просто повторно посилання на оновлену бібліотеку об'єктів (якщо це те, що він є) може бути досить.


2

Примітка. Наступний перелік не буде вичерпним. Правки вітаються!

Застосовувані сценарії включають:

  • Багатомодульні програми, де з певних причин ви не бажаєте перекомпіляції.
  • Структури, використовувані в бібліотеках, де ви не хочете змушувати користувачів бібліотеки перекомпілювати кожного разу, коли ви змінюєте (опубліковану) структуру.
  • Структури, що містять різні елементи на різних платформах, на яких працює модуль.

Найвідоміша така структура FILE. Ви просто зателефонували fopen()та отримаєте вказівник у разі успіху. Потім цей покажчик передається одна одній функції, яка працює над файлами. Але ви не знаєте - і не хочете знати - деталей, як, наприклад, містять елементи та розмір.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.