C语言的面向对象编程(一)

时间:2022-10-11 16:56:17

一、前言

对于编程而言,重要的是解决问题的方式,而不是语言本身。面向对象与面向过程是解决问题和思考问题的方式。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》。