首页 > 解决方案 > 什么是 C 中的编译时封装?

问题描述

当我研究 C 相对于 C++ 的优势时,我遇到了这段:

C 中进行封装的标准方法是转发声明一个结构,并且只允许通过函数访问其数据。此方法还创建编译时封装。编译时封装允许我们更改数据结构成员,而无需重新编译客户端代码(使用我们接口的其他代码)。另一方面,进行封装 C++ 的标准方法(使用类)需要在添加或删除私有成员变量时重新编译客户端代码。

我了解如何通过函数向前声明结构并访问其成员隐藏结构的实现细节。我不明白的是这一行:

编译时封装允许我们更改数据结构成员,而无需重新编译客户端代码(使用我们接口的其他代码)。

这适用于什么场景?

标签: c

解决方案


可能发生这种情况的实际情况是,在硬盘空间非常有限的日子编写的数据库库使用单个字节来存储日期的“年”字段(例如 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 安全)版本时,无需更改代码。事实上,您甚至可能不必重新编译:只需重新链接到更新的对象库(如果是这样的话)足够了。


推荐阅读