一.封装(C面向对象开发)

内容参考于《抽象接口技术和组件开发规范及其思想》

一.封装

在C语言中,可以使用一个C文件(.c 文件)和H文件完(*.h 文件)成“类”的定义,将所有需要封装的东西都存于C 文件中,H 文件中只展现那些对外可见、无需封装(重点)的内容。

1. 示例1:

一个栈对象的例子。
stack.h

#ifndef __STACK_H
#define __STACK_H

struct stack; /* 类型声明,无需关心类定义的具体细节 */
struct stack * stack_create (int size); /* 创建栈,并指定栈空间的大小 */
int stack_push (struct stack *p_stack, int val); /* 入栈 */
int stack_pop (struct stack *p_stack, int *p_val); /* 出栈 */
int stack_delete (struct stack *p_stack); /* 删除栈 */

#endif

stack.c

#include "stack.h"
#include "stdlib.h" /* for malloc */

struct stack {
	int top; /* 栈顶 */
	int *p_buf; /* 栈缓存 */
	unsigned int size; /* 栈缓存的大小 */
};

struct stack * stack_create(int size)
{
	struct stack *p_stack = (struct stack *)malloc(sizeof(struct stack));
	if (p_stack != NULL) {
		p_stack->top = 0;
		p_stack->size = size;
		p_stack->p_buf = (int *)malloc(sizeof(int) * size);
		if (p_stack->p_buf != NULL) {
		}
		free(p_stack); /* 分配栈内存失败 */
	}
	return NULL; /* 创建栈失败, 返回 NULL */
}

int stack_push(struct stack *p_stack, int val)
{
	if (p_stack->top != p_stack->size) {
		p_stack->p_buf[p_stack->top++] = val;
		return 0;
	}
	return -1;
}

int stack_pop(struct stack *p_stack, int *p_val)
{
	if (p_stack->top != 0) {
		*p_val = p_stack->p_buf[--p_stack->top];
		return 0;
	}
	return -1;
}

int stack_delete(struct stack *p_stack)
{
	if (p_stack == NULL) {
		return -1;
	}
	if (p_stack->p_buf != NULL) {
		free(p_stack->p_buf);
	}
	free(p_stack);
	return 0;
}

main.c

#include "stack.h"
#include "stdio.h"

int main()
{
	int val;
	struct stack *p_stack = stack_create(20);

	// 依次压入数据: 2、 4、 5、 8、 10
	stack_push(p_stack, 2);
	stack_push(p_stack, 4);
	stack_push(p_stack, 5);
	stack_push(p_stack, 8);
	stack_push(p_stack, 10);

	// 依次弹出各个数据, 并打印
	stack_pop(p_stack, &val); printf("%d ", val);
	stack_pop(p_stack, &val); printf("%d ", val);
	stack_pop(p_stack, &val); printf("%d ", val);
	stack_pop(p_stack, &val); printf("%d ", val);
	stack_pop(p_stack, &val); printf("%d ", val);

	stack_delete(p_stack);
	return 0;
}
  1. 类具有属性,它是对数据(对象的状态) 的抽象。在 C 程序设计时, 通常使用结构体类型来表示一个类,相关属性即包含在相应的结构体类型中。
  2. 类具有方法,它是对象行为的抽象,用方法名和实现该操作的方法来描述。在 C 程序设计中,方法可以看作普通的函数,不过其通常有一个特点: 函数的第一个参数,为“类”类型的指针,该指针指向了一个确定的对象,用以表明此次操作要作用于哪个对象, 在方法实现时,即可通过该指针访问到对象中的各个属性。
  3. stack.h 的程序是没有 struct stack 结构体成员的访问权限的,它们只能调用stack.h 文件中声明的方法: 创建(stack_create)、 删除(stack_delete)、 入栈(stack_push) 、出栈(stack_pop)。对于外界用户来说, struct stack 这个结构体的内部细节,以及各个函数的具体实现方式都是不可见的。 这正是完美的封装!
2. 扩展

上面的示例中是通过malloc去获取内存,内存来源是受到限制的,虽然上面示例完美得展现了封装,可是更多时候会使用以下的形式(建议都使用这种)。
stack.h

#ifndef __STACK_H
#define __STACK_H

struct stack { /* 类型定义 */
	int top; /* 栈顶 */
	int *p_buf; /* 栈缓存 */
	unsigned int size; /* 栈缓存的大小 */
};

int stack_init(struct stack *p_stack, int *p_buf, int size); /* 初始化 */
int stack_push(struct stack *p_stack, int val); /* 入栈 */
int stack_pop(struct stack *p_stack, int *p_val); /* 出栈 */
int stack_deinit(struct stack *p_stack); /* 解初始化 */

#endif

stack.c

#include "stack.h"

int stack_init(struct stack *p_stack, int *p_buf, int size)
{
	p_stack->top = 0;
	p_stack->size = size;
	p_stack->p_buf = p_buf;
	return 0;
}

int stack_push(struct stack *p_stack, int val)
{
	if (p_stack->top != p_stack->size) {
		p_stack->p_buf[p_stack->top++] = val;
		return 0;
	}
	return -1;
}

int stack_pop(struct stack *p_stack, int *p_val)
{
	if (p_stack->top != 0) {
		*p_val = p_stack->p_buf[--p_stack->top];
		return 0;
	}
	return -1;
}

int stack_deinit(struct stack *p_stack)
{
	return 0;
}

main.c

#include "stack.h"
#include "stdio.h"

int main()
{
	int val;
	int buf[20];
	struct stack stack;
	struct stack *p_stack = &stack;
	stack_init(p_stack, buf, 20);

	// 依次压入数据: 2、 4、 5、 8、 10
	stack_push(p_stack, 2);
	stack_push(p_stack, 4);
	stack_push(p_stack, 5);
	stack_push(p_stack, 8);
	stack_push(p_stack, 10);

	// 依次弹出各个数据, 并打印
	stack_pop(p_stack, &val); printf("%d ", val);
	stack_pop(p_stack, &val); printf("%d ", val);
	stack_pop(p_stack, &val); printf("%d ", val);
	stack_pop(p_stack, &val); printf("%d ", val);
	stack_pop(p_stack, &val); printf("%d ", val);

	stack_deinit(p_stack);
	return 0;
}
  1. 对象的创建交给了用户,用户喜欢怎么创建都行,把对象和相相关参数提供给init初始化方法就行。所以不再需要create和delete,需要的是init和deinit(构造函数和析构函数)
  2. 很明显new一个对象,使用的是示例一的方法,而这里可以new也可以直接定义,把权力交给了用户。
3. 个人观点

C同样是可以面向对象的,只不过因为编译器不做限制,所以很多不面向对象的行为能够编译通过。所以这时候需要程序员自身的修养进行保证,例如上面,程序员保证不去访问对象的成员,那么成员就是封装的。可是总有些没修养的程序员喜欢去访问对象的成员,所以出来了C++编译器去禁止这种行为,只要访问了私有的成员,那么编译器就报错。所以…(好了,这是开玩笑,供C开发者yy)。

我阅读过minix的代码,作者为了加强C的面向对象,定义了如下宏

#define PUBLIC ""
PUBLIC int a;

虽然最后预处理后还是int a;可是通过宏进行了表述,更加明显得表达了封装的特性。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值