一、前言
对于编程而言,重要的是解决问题的方式,而不是语言本身。面向对象与面向过程是解决问题和思考问题的方式。C语言常说是面向过程开发的语言,因为其缺少很多对于面向对象特性的支持。但,这并不影响我们采用面向对象的方式,使用C语言编程。
二、基本介绍
在C语言中,采用面向对象开发,有两个地方是要明白的:
1、结构体,
2、函数指针变量。
结构体:结构体是对于内存的一种组织方式,结构体成员可以是多个不同类型的变量(这是与数组最大的区别,数组中可以包含多个的变量,但只能是同一种类型)。这样使用结构体做到将多个相关的数据集合(封装)到一起。便于记忆与管理。
函数指针变量:函数指针变量,是一个可以存放函数地址的指针变量。因为它是变量,所以可以被重新赋值;因为它是指针变量,所以可以间接寻址;因为它是函数指针变量,可以将函数地址赋值给它,并通过对它的访问,实现调用函数的目的。
由于结构体本身不能定义函数,那么没有办法将函数直接集合(封装)到结构体中。但是,函数指针变量给我们提供了一个间接将函数集合(封装)到一起的方式。我们可以通过调用函数指针变量,实现调用函数的目的。
struct struct_test {
uint8_t type;
uint16_t counter;
uint32_t size;
void (*fun)(void *input);
};
struct struct_test test;
测试内存(Ubuntu gcc编译)分布如下:
test size:12
type:0xbff9fb64 /* 这里进行了字节对齐,多加了一个字节 */
counter:0xbff9fb66
size:0xbff9fb68
fun:0xbff9fb6c
三、简单实现
面向对象的本质是对数据和方法(函数)的隐藏,避免对细节的了解。而这一点,正是结构体结合函数指针变量可以做到的。
在这里,着重于对于数据和方法(函数)的隐藏来谈论C语言面面向对象的简单实现。暂时不去谈论,关于面向对象的其他特性例如:继承,组合,多态等。
在C的模块化编程中,将一个.c和.h文件看作一个模块,在实现的时候,尽量使用局部静态变量,减少对外的函数接口,同时尽可能少的调用其它模块中的函数,避免受到其他模块的影响(也即,低耦合,高内聚)。C的面向对象编程与模块化编程,都是为了解决降低编程复杂度,减轻同一时间需要记忆的工作量,所以他们可以也应该联合使用。
简单的例子:
有一个.c文件oo_test.c
#include "oo_test.h"
static struct oo_test oo;
/* struct oo_test oo; */
/*
void oo_test_init(void)
{
oo.type = 0;
oo.counter = 0;
oo.fun = &test;
}
*/
struct oo_test *oo_test_init(void)
{
oo.type = 0;
oo.counter = 0;
oo.fun = &test;
return &oo;
}
static void test(void *input)
{
…;
}
有一个.h文件oo_test.h
#ifndef OO_TEST_H
#define OO_TEST_H
#include "stdint.h"
struct oo_test {
uint8_t type;
uint16_t counter;
void (*fun)(void *input);
};
struct oo_test *oo_test_init(void);
/* #define OO oo */
/* extern struct oo_test oo; */
#endif
其他文件使用:
xx.c
……
struct oo_test *oo = oo_test_init();
oo.fun();
/* oo_test_init(); */
/* OO.fun(); */
……
(注释中,是另一个种实现方式,这两种方式各有所取。根据使用的情况下,适当选择)
在上边提到的简单的例子中,首先避免了C编程中全局变量的问题,通过局部静态变量实现信息传递,做到数据的隐藏;
其次减少C编程中对外的函数接口(这一点是模块化编程,与面向对象编程都强调的一点:减少对外的接口,隐藏内部函数)。
四、参考
Contiki操作系统;
《Make Embedded System》。