一 概述
我们经常说C语言是面向过程的,而C++、C#、Java等编程语言是面向对象(OOP)的。
面向对象编程有诸多优点,举个例子说,我们装修好新房,准备去买台电视机。现在有两个电器商城,一个商城是面向对象的,一个商城是面向过程的。面向对象的商城提供的是一台完整的电视机,而且提供一份说明书,说明书说明了这台电视尺寸、重量、颜色、分辨率等属性,还规定了电视的开机、直播、联网、关机等方法,你拿到电视机和说明书后,只要按照说明书的要求去操作电视就好了。而面向过程的商城不直接提供电视整机,它给你一堆元器件、显示屏、机壳、安装架等零件,并提供了电视的安装图纸,叫你把这些东西拿回去自己组装测试,折腾成功后才能使用。请问,你会选择哪一个商城?
所以,面向对象编程的优点是不言而喻的。面向过程的C语言设计相对复杂,但执行效率较高,在嵌入式开发领域至今仍是C的天下,暂时是不可能被oop语言取代的。
那么有没有一种方法让C语言也实现类似面向对象的功能呢,如果能将两者的优点结合起来岂不是美事一桩?我们今天就来探究一下。
二 实现方法
在C语言中有一种数据类型与OOP编程语言中的“类(Class)”比较相似,对,就是结构体(Struct),我们就从它入手。
首先定义一个结构体类型
typedefstruct bird
{
char name[100];
char add[100];
float weight;
}_Bird;
我们定义了一个名字为_Bird类型的结构体,它有3个变量,分别是名字name,地址add和重量weight,我们可以把这个3个变量看作oop语言中的字段或者属性。
以上我们定义了一个结构体,模拟了类的属性概念。我们知道,类还有一个特性就是具有方法,那么在C的结构体中怎么包含方法呢?敲黑板敲黑板敲黑板重点来了,我们想起了C语言的核心概念—指针,没错就是指针,我们可以在结构体中使用函数指针,然后将这个函数指针指向具备执行功能的函数,只要在结构体初始化时将结构体的函数指针和执行函数联系起来,那么这个函数指针的功能就模拟oop语言中的方法成员了。
我们继续完善结构体的定义:
typedefstruct bird
{
char name[100];
char add[100];
float weight;
void (*fly)(double);
void (*eat)();
}_Bird;
这里我们增加了两个函数指针(*fly)和(*eat),其中(*fly)可以指向一个形参为double 无返回值的函数,(*eat)指向既无形参也无返回值的函数,当然你可以定义任意符合要求的函数指针。
上面说道,我们有了函数指针,还需要具体的函数供函数指针调用,直接写就好。
void BirdEat()
{
printf("I eat caterpillar!\n");
}
void BirdFly(double x)
{
printf("I can fly %.0f km!\n",x);
}
有了指针和函数,我们还要把它们结合起来,也就是要把函数地址赋值给指针,这是结构体初始化时做的事情。我们可以模拟oop语言中的New功能,即实例化一个对象。我们这样写_Bird结构体的初始化函数:
_BirdNew_Bird()
{
_Bird b;
strcpy(b.name,"Wild goose");
strcpy(b.add,"Siberia");
b.weight=12;
b.fly=BirdFly;
b.eat=BirdEat;
return b;
}
这个模拟New功能的函数做了以下几件事:
1)对_Bird类型变量b的成员变量进行了初始化
strcpy(b.name,"Wild goose");
strcpy(b.add,"Siberia");
b.weight=12;
2)将结构体_Bird的函数指针成员变量进行赋值,即将函数指针指向了实际可执行的函数(斜体红色部分),也可以理解为oop语言中的类具备了方法成员。
3)返回一个_Bird类型的变量b
经过以上3步,只要调用这个New_Bird()函数就可以得到一个_Bird类型的对象,并进行了初始化。
下面我们来写一个主函数测试一下:
int main (int argc, char*argv[])
{
if (InitCVIRTE (0, argv, 0) == 0)
return -1; /* out of memory */
_Bird bird1=New_Bird(); //定义一个_Bird类型的变量并初始化
printf("Bird Name:%s\n",bird1.name); //打印名字
printf("Bird Add:%s\n",bird1.add); //打印栖息地
printf("Bird Weight:%0.f\n",bird1.weight); //打印体重
bird1.fly(100.0); //执行fly方法
bird1.eat(); //执行eat方法
getchar();
return0;
}
我们看看执行结果
_Bird中定义的方法都得到了执行,完全符合面向对象的设计思想。
实际设计时,我们可以单独设计结构体_Brid的C和h文件,将结构体的定义放在h文件中,将new方法和其他成员方法都放在C文件中,这样看起来就与oop语言中的类完全一致。
三 结束语
哇,真是太棒了。