Đóng gói thời gian biên dịch trong C là gì?


9

Khi tôi đang nghiên cứu những lợi thế của C so với C ++, tôi đã xem qua đoạn này:

Cách tiêu chuẩn trong C để thực hiện đóng gói là chuyển tiếp khai báo một cấu trúc và chỉ cho phép truy cập vào dữ liệu của nó thông qua các hàm. Phương pháp này cũng tạo ra đóng gói thời gian biên dịch. Biên dịch đóng gói thời gian cho phép chúng tôi thay đổi các thành viên cấu trúc dữ liệu mà không cần biên dịch lại mã máy khách (mã khác sử dụng giao diện của chúng tôi). Mặt khác, cách đóng gói C ++ tiêu chuẩn (sử dụng các lớp) đòi hỏi phải biên dịch lại mã máy khách khi thêm hoặc xóa các biến thành viên riêng.

Tôi hiểu cách khai báo một cấu trúc và truy cập các thành viên của nó thông qua các hàm ẩn các chi tiết triển khai của cấu trúc. Điều tôi không hiểu là dòng này cụ thể:

Biên dịch đóng gói thời gian cho phép chúng tôi thay đổi các thành viên cấu trúc dữ liệu mà không cần biên dịch lại mã máy khách (mã khác sử dụng giao diện của chúng tôi).

Trong kịch bản này được áp dụng?


Về cơ bản, structlà một hộp đen với nội bộ không rõ. Nếu khách hàng không biết nội bộ, nó không bao giờ có thể truy cập trực tiếp vào chúng và bạn có thể thay đổi chúng theo ý muốn. Điều này tương tự như đóng gói trong OOP. Các phần bên trong là riêng tư và bạn chỉ thay đổi đối tượng bằng các phương thức công khai.
Sulthan

Điêu nay không phải luc nao cung đung. Nếu bạn quyết định thêm / xóa thành viên của một cấu trúc, bạn thay đổi kích thước của nó. Điều này sẽ yêu cầu biên dịch lại mã máy khách.
DarkAtom

2
@DarkAtom Không đúng! Nếu khách hàng không biết nội dung ( cấu trúc mờ ), thì nó không biết kích thước của nó, vì vậy việc thay đổi kích thước không phải là vấn đề.
Adrian Mole

1
@DarkAtom: Cho phép truy cập vào một cấu trúc chỉ thông qua các chức năng chỉ bao gồm phân bổ thông qua các chức năng. Thư viện sẽ cung cấp một chức năng để phân bổ một cấu trúc và khách hàng sẽ không bao giờ biết kích thước của nó. Thay đổi kích thước không yêu cầu biên dịch lại máy khách.
Eric Postpischil

3
Lưu ý rằng về mặt kỹ thuật, đây không phải là "lợi thế của C so với C ++" như bạn có thể (và thường làm) thực hiện cùng một ý tưởng trong C ++. Tra cứu thành ngữ "pimpl" .
dùng4815162342

Câu trả lời:


4

Một kịch bản trong thế giới thực có thể xảy ra là khi thư viện cơ sở dữ liệu, được viết vào những ngày mà không gian đĩa cứng rất hạn chế, đã sử dụng một byte duy nhất để lưu trữ trường 'năm' của một ngày (ví dụ: 11-NOV-1973 sẽ có 73trong năm). Nhưng, khi Năm 2000 xuất hiện, điều này sẽ không còn đủ nữa và năm đó phải được lưu trữ dưới dạng một số nguyên ngắn (16 bit). Tiêu đề có liên quan (đơn giản hóa nhiều) cho thư viện này có thể là:

// dbEntry.h
typedef struct _dbEntry dbEntry;

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

Và chương trình 'khách hàng' sẽ là:

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

Việc thực hiện 'bản gốc':

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

Sau đó, theo cách tiếp cận của Y2K, tệp triển khai này sẽ được thay đổi như sau (mọi thứ khác không bị ảnh hưởng):

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

Khi khách hàng cần được cập nhật để sử dụng phiên bản mới (Y2K an toàn), sẽ không yêu cầu thay đổi mã. Trong thực tế, bạn thậm chí có thể không phải biên dịch lại: chỉ cần liên kết lại với thư viện đối tượng được cập nhật (nếu đó là những gì) có thể là đủ.


2

Lưu ý: Danh sách sau đây sẽ không đầy đủ. Chỉnh sửa được chào đón!

Các kịch bản áp dụng bao gồm:

  • Các ứng dụng đa mô-đun mà bạn không muốn biên dịch lại vì một số lý do.
  • Các cấu trúc được sử dụng trong các thư viện nơi bạn không muốn buộc người dùng của thư viện biên dịch lại mỗi khi bạn thay đổi cấu trúc (đã xuất bản).
  • Các cấu trúc có chứa các yếu tố khác nhau trên các nền tảng khác nhau mà mô-đun hoạt động.

Cấu trúc được biết đến nhiều nhất của loại này là FILE. Bạn chỉ cần gọi fopen()và nhận được một con trỏ nếu thành công. Con trỏ này sau đó được bàn giao cho nhau chức năng hoạt động trên các tệp. Nhưng bạn không biết - và bạn không muốn biết - các chi tiết, như các yếu tố có chứa và kích thước.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.