การห่อหุ้มเวลาคอมไพล์ใน C คืออะไร?


9

เมื่อฉันค้นคว้าข้อดีของ C เหนือ C ++ ฉันเจอในย่อหน้านี้:

วิธีมาตรฐานใน C เพื่อทำ encapsulation คือการส่งต่อประกาศโครงสร้างและอนุญาตการเข้าถึงข้อมูลผ่านฟังก์ชันเท่านั้น วิธีนี้ยังสร้างการห่อหุ้มเวลาการคอมไพล์ การคอมไพล์เวลาการคอมไพล์ช่วยให้เราสามารถเปลี่ยนสมาชิกโครงสร้างข้อมูลโดยไม่ต้องคอมไพล์รหัสไคลเอนต์ (รหัสอื่นโดยใช้อินเตอร์เฟสของเรา) วิธีมาตรฐานในการทำ encapsulation C ++ ในทางกลับกัน (การใช้คลาส) ต้องมีการคอมไพล์ใหม่ของรหัสลูกค้าเมื่อเพิ่มหรือลบตัวแปรสมาชิกส่วนตัว

ฉันเข้าใจว่าการประกาศโครงสร้างและการเข้าถึงสมาชิกผ่านฟังก์ชั่นจะซ่อนรายละเอียดการใช้งานของโครงสร้างไว้อย่างไร สิ่งที่ฉันไม่เข้าใจคือบรรทัดนี้โดยเฉพาะ:

คอมไพล์เวลา encapsulation ช่วยให้เราสามารถเปลี่ยนสมาชิกในโครงสร้างข้อมูลโดยไม่ต้อง recompilation รหัสลูกค้า (รหัสอื่น ๆ ที่ใช้อินเตอร์เฟซของเรา)

สิ่งนี้เกี่ยวข้องกับสถานการณ์ใดบ้าง?


โดยพื้นฐานแล้วstructกล่องสีดำที่มีตัวระบุไม่ทราบ หากลูกค้าไม่รู้จัก internals จะไม่สามารถเข้าถึงพวกเขาได้โดยตรงและคุณสามารถเปลี่ยนพวกเขาได้ตามต้องการ คล้ายกับ encapsulation ใน OOP internals เป็นแบบส่วนตัวและคุณเปลี่ยนวัตถุโดยใช้วิธีสาธารณะเท่านั้น
Sulthan

สิ่งนี้ไม่เป็นความจริงเสมอไป หากคุณตัดสินใจที่จะเพิ่ม / ลบสมาชิกของ struct คุณจะเปลี่ยนขนาดของมัน สิ่งนี้จะต้องมีการคอมไพล์ใหม่ของรหัสลูกค้า
DarkAtom

2
@DarkAtom ไม่เป็นความจริง! หากลูกค้าไม่ทราบเนื้อหา ( โครงสร้างทึบแสง ) ก็ไม่ทราบขนาดของมันดังนั้นการเปลี่ยนขนาดจะไม่เป็นปัญหา
Adrian Mole

1
@DarkAtom: การอนุญาตให้เข้าถึงโครงสร้างผ่านฟังก์ชันเท่านั้นรวมถึงการจัดสรรผ่านฟังก์ชันเท่านั้น ห้องสมุดจะให้ฟังก์ชั่นในการจัดสรรโครงสร้างและลูกค้าจะไม่ทราบขนาดของมัน การเปลี่ยนขนาดไม่จำเป็นต้องทำการคอมไพล์ไคลเอนต์ใหม่
Eric Postpischil

3
โปรดทราบว่านี่ไม่ใช่เทคนิค "ประโยชน์ของ C มากกว่า C ++" ตามที่คุณสามารถ (และมักจะทำ) ใช้แนวคิดเดียวกันใน C ++ เงยหน้าขึ้นมอง"pimpl" สำนวน
user4815162342

คำตอบ:


4

สถานการณ์ในโลกแห่งความเป็นจริงที่เป็นไปได้ซึ่งจะเกิดขึ้นคือเมื่อห้องสมุดฐานข้อมูลเขียนในวันที่เนื้อที่บนฮาร์ดดิสก์มี จำกัด มากใช้ไบต์เดียวเพื่อเก็บฟิลด์ 'ปี' ของวันที่ (เช่น 11-NOV-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-safe) จะไม่จำเป็นต้องมีการเปลี่ยนแปลงรหัส ในความเป็นจริงคุณอาจไม่จำเป็นต้องคอมไพล์ใหม่อีกครั้ง: เพียงแค่เชื่อมโยงไปยังไลบรารีวัตถุที่อัปเดตใหม่ (ถ้านั่นคือสิ่งที่มันเป็น) ก็เพียงพอแล้ว


2

หมายเหตุ: รายการต่อไปนี้จะไม่หมดจด ยินดีต้อนรับการแก้ไข!

สถานการณ์ที่เกี่ยวข้องรวมถึง:

  • แอปพลิเคชั่นหลายโมดูลที่คุณไม่ต้องการคอมไพล์ซ้ำด้วยเหตุผลบางประการ
  • โครงสร้างที่ใช้ในไลบรารีที่คุณไม่ต้องการบังคับให้ผู้ใช้ของไลบรารีคอมไพล์ใหม่ทุกครั้งที่คุณเปลี่ยนโครงสร้าง (เผยแพร่)
  • โครงสร้างที่ประกอบด้วยองค์ประกอบต่าง ๆ บนแพลตฟอร์มต่าง ๆ ที่โมดูลทำงาน

FILEโครงสร้างที่รู้จักมากที่สุดของชนิดนี้คือ คุณเพียงแค่โทรหาfopen()และรับตัวชี้ถ้าสำเร็จ ตัวชี้นี้จะถูกส่งมอบให้กับแต่ละฟังก์ชั่นอื่น ๆ ที่ทำงานกับไฟล์ แต่คุณไม่รู้ - และคุณไม่ต้องการที่จะรู้รายละเอียดเช่นองค์ประกอบที่มีอยู่และขนาด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.