c 中的完美封装

面向对象编程为程序员的兵器库中添加了很多重型武器,比如:封装,多态等。

通过封装,我们的模块可以强制所有的访问都使用其提供的接口,而不用对外暴露模块内部实现细节,同时也免于其自身状态被"出乎意料"的破坏。典型的做法就是将一个结构体的所有数据字段声明为 private,让其他模块只能通过其提供的 public 函数接口访问:

class MyClass {
 public:
    int get_data() const {
        return data_;
    }

    int set_data(int d) {
        data_ = d;
    }

 private:
    int data_;
};


这样一来,对成员数据的访问、更新被限制到了两个接口内,我们可以其中进行任意的扩展、限制。比如可以对更新进行一些合法性的校验等。但是这样做我们其实还是将 c++ class 的数据成员暴露给了外部(这是编译器要求)。
这在类似 c++ 这在支持对象对象编程的语言中,是一种很自然的做法。那么在 c 语言这种,语言本身没有提供封装的语言中,我们有什么办法吗?答案是:有,而且可以做到"完美封装".

// interface.h
typedef struct _my_class_t *my_class_t;

my_class_t my_class_new();
extern void my_class_free(my_class_t);
extern int get_data(my_class_t c);
extern void set_data(my_class_t c, int data);


首先我们会提供一个接口头文件,其他模块为了使用我们的 MyClass 都必须包含,也仅应该包含这个头文件。
然后我们真正的实现我们的 struct:

// data.h
typedef struct _my_class {
    int data;
} _my_class_t;

// data.c
_my_class_t my_class_new() {
    _my_class_t *d = malloc(sizeof(_my_class_t));
    return d;
}

void my_class_free(_my_class_t *d) {
    free(d);
}

int get_data(_my_class_t *d) {
    return d->data;
}

void set_data(_my_class_t *d, int data) {
    d->data = data;
}


通过将一个 incomplete-type(即编译器在编译某单元时,未见到该 struct 的完整定义) pointer 作为一个 handle 的角色。我们可以在让调用者在对我们自定义结构体一无所知的情况下,使用我们提供的接口,调用者甚至都不会知道 my_class_t 中包含了一个 int 数据!

如果这不是完美封装,那么什么是完美封装?

当然 c++ 也可以如法炮制,实现完美封装,但是因为使用 c++ 时我们往往会将 class 相关的操作作为 class 的成员函数提供给调用者。所以调用者往往需要将 class 头文件包含进去,这样就必须将 class 的数据成员暴露给调用者,反而损失了一定的封装性。当然这个问题可以通过 impl-handle 技法避免,但是也要额外引入一些工作量。

当然这种方式实现的完美封装也有一些副作用,比如你无法在栈上定义一个 my_class_t 对象,这是因为编译器在编译时没有看到(我们仅提供了 interface.h 文件,还记得吧?)完整的 my_class_t 定义。但是如果使用 c++ class 则没有这个问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值