c语言中的类模拟(c++编程思想),C语言中的类模拟(C++编程思想)

所谓类:是对特定数据的特定操作的集合体。所以说类包含了两个范畴:数据和操作。而C语言中的struct仅仅是数据的集合。

1.实例:下面先从一个小例子看起

#ifndef C_Class

#define C_Class struct

#endif

C_Class A {

C_Class A *A_this;

void (*Foo)(C_Class A *A_this);

int a;

int b;

};

C_Class B{ //B继承了A

C_Class B *B_this; //顺序很重要

void (*Foo)(C_Class B *Bthis); //虚函数

int a;

int b;

int c;

};

void B_F2(C_Class B *Bthis)

{

printf("It is B_Fun\n");

}

void A_Foo(C_Class A *Athis)

{

printf("It is A.a=%d\n",Athis->a);//或者这里

// exit(1);

// printf("纯虚 不允许执行\n");//或者这里

}

void B_Foo(C_Class B *Bthis)

{

printf("It is B.c=%d\n",Bthis->c);

}

void A_Creat(struct A* p)

{

p->Foo=A_Foo;

p->a=1;

p->b=2;

p->A_this=p;

}

void B_Creat(struct B* p)

{

p->Foo=B_Foo;

p->a=11;

p->b=12;

p->c=13;

p->B_this=p;

}

int main(int argc, char* argv[])

{

C_Class A *ma,a;

C_Class B *mb,b;

A_Creat(&a);//实例化

B_Creat(&b);

mb=&b;

ma=&a;

ma=(C_Class A*)mb;//引入多态指针

printf("%d\n",ma->a);//可惜的就是 函数变量没有private

ma->Foo(ma);//多态

a.Foo(&a);//不是多态了

B_F2(&b);//成员函数,因为效率问题不使用函数指针

return 0;

}

输出结果:

11

It is B.c=13

It is A.a=1

It is B_Fun

2.类模拟解说:

我在网上看见过一篇文章讲述了类似的思想(据说C++编程思想上有更加详细的解说,

可惜我没空看这个了,如果有知道的人说一说吧)。但是就象C++之父说的:“C++和C是两种

语言”。所以不要被他们在语法上的类似就混淆使用,那样有可能会导致一些不可预料的事情

发生。

其实我很同意这样的观点,本文的目的也不是想用C模拟C++,用一个语言去模拟另外

一个语言是完全没有意义的。我的目的是想解决C语言中,整体框架结构过于分散、以及数据

和函数脱节的问题。

C语言的一大问题是结构松散,虽然现在好的大型程序都基本上按照一个功能一个文件的

设计方式,但是无法做到更小的颗粒化――原因就在于它的数据和函数的脱节。类和普通的

函数集合的最大区别就在于这里。类可以实例化,这样相同的函数就可以对应不同的实例化

类的变量。

自然语言的一个特点是概括:比如说表。可以说手表,钟表,秒表等等,这样的描述用

面向对象的语言可以说是抽象(继承和多态)。但是我们更要注意到,即使对应于手表这个

种类,还是有表链的长度,表盘的颜色等等细节属性,这样细微的属性如果还用抽象,就无

法避免类膨胀的问题。所以说类用成员变量来描述这样的属性。这样实例并初始化不同的

类,就描述了不同属性的对象。

但是在C语言中,这样做是不可能的(至少语言本身不提供这样的功能)。C语言中,如

果各个函数要共享一个变量,必须使用全局变量(一个文件内)。但是全局变量不能再次实

例化了。所以通常的办法是定义一个数组。以往C语言在处理这样的问题的时候通常的办法就

是这样,比如说socket的号,handel等等其实都是数组的下标。(不同的连接对应不同的

号,不同的窗口对应不同的handel,其实这和不同的类有不同的成员变量是一个意思)

个人认为:两种形式(数组和模拟类)并无本质的区别(如果不考虑虚函数的应用的

话),它们的唯一区别是:数组的办法将空间申请放在了“模块”内,而类模拟的办法将空间

申请留给了外部,可以说就这一点上,类模拟更加灵活。

3.其他的话:

我的上述思想还是很不成熟的,我的目的是想让C语言编程者能够享受面向对象编程的更

多乐趣。我们仅仅面对的是浩瀚的“黑箱”,我们的工作是堆砌代码,而且如果要更改代码功

能的时候,仅仅换一个黑箱就可以了。

而更大的目的是促使这样的黑箱的产生。或许有一天,一种效率很好,结构很好的语言

将会出现。那个时候编程是不是就会象说话一样容易了呢?

相信很多人都看过设计模式方面的书,大家有什么体会呢?Bridge,Proxy,Factory这些设

计模式都是基于抽象类的。使用抽象对象是这里的一个核心。

其实我觉得框架化编程的一个核心问题是抽象,用抽象的对象构建程序的主体框架,这

是面向对象编程的普遍思想。用抽象构建骨架,再加上多态就形成了一个完整的程序。由于C

++语言本身实现了继承和多态,使用这样的编程理念(理念啥意思?跟个风,嘿嘿)在C+

+中是十分普遍的现象,可以说Virtual(多态)是VC的灵魂。

但是,使用C语言的我们都快把这个多态忘光光了。我常听见前辈说,类?多态?我们用

的是C,把这些忘了吧。很不幸的是,我是一个固执的人。这么好的东西,为啥不用呢。很高

兴的,在最近的一些纯C代码中,我看见了C中的多态!下面且听我慢慢道来。

1. VC中的Interface是什么

Interface:中文解释是接口,其实它表示的是一个纯虚类。不过我所要说的是,在VC中

的Interface其实就是struct,查找Interface的定义,你可以发现有这样的宏定义:

#Ifndef Interface

#define Interface struct

#endif

而且,实际上在VC中,如果一个类有Virtual的函数,则类里面会有vtable,它实际上是一个

虚函数列表。实际上C++是从C发展而来的,它不过是在语言级别上支持了很多新功能,在C

语言中,我们也可以使用这样的功能,前提是我们不得不自己实现。

2.C中如何实现纯虚类(我称它为纯虚结构)

比较前面,相信大家已经豁然开朗了。使用struct组合函数指针就可以实现纯虚类。

例子: typedef struct {

void (*Foo1)();

char (*Foo2)();

char* (*Foo3)(char* st);

}MyVirtualInterface;

这样假设我们在主体框架中要使用桥模式。(我们的主类是DoMyAct,接口具体实现类是

Act1,Act2)下面我将依次介绍这些“类”。(C中的“类”在前面有说明,这里换了一个,是使

用早期的数组的办法)

主类DoMyAct: 主类中含有MyVirtualInterface* m_pInterface; 主类有下函数:

DoMyAct_SetInterface(MyVirtualInterface* pInterface)

{

m_pInterface= pInterface;

}

DoMyAct_Do()

{

if(m_pInterface==NULL) return;

m_pInterface->Foo1();

c=m_pInterface->Foo2();

}

子类Act1:实现虚结构,含有MyVirtualInterface st[MAX]; 有以下函数:

MyVirtualInterface* Act1_CreatInterface()

{

index=FindValid() //对象池或者使用Malloc !应该留在外面申请,实

例化

if(index==-1) return NULL;

St[index].Foo1=Act1_Foo1; // Act1_Foo1要在下面具体实现

St[index].Foo2=Act1_Foo2;

St[index].Foo3=Act1_Foo3;

Return &st [index];

}

子类Act2同上。

在main中,假设有一个对象List。List中存贮的是MyVirtualInterface指针,则有:

if( (p= Act1_CreatInterface()) != NULL)

List_AddObject(&List, p); //Add All

While(p=List_GetObject()){

DoMyAct_SetInterface(p);//使用Interface代替了原来大篇幅的Switch Case

DoMyAct_Do();//不要理会具体的什么样的动作,just do it

}

FREE ALL。

在微系统里面,比如嵌入式,通常使用对象池的技术,这个时候可以不用考虑释放的问

题(对象池预先没有空间,使用Attach,在某个函数中申请一个数组并临时为对象池分配空

间,这样函数结束,对象池就释放了)

但是在Pc环境下,由于程序规模比较大,更重要的是一些特殊的要求,使得对象的生命

周期必须延续到申请的那个函数体以外,就不得不使用malloc,实际上即使在C++中,new

对象的自动释放始终是一个令人头疼的问题,新的标准引入了智能指针。但是就我个人而

言,我觉得将内存释放的问题完全的交给机器是不可信任的,它只能达到准最佳。

你知道设计Java的垃圾回收算法有多困难吗?现实世界是错综复杂的,在没有先验条件

下,要想得到精确的结果及其困难。所以我说程序员要时刻将free记在心上,有关程序的健

壮性和自我防御将在另外一篇文章中讲述。

3.纯虚结构的退化

下面我们来看看如果struct里面仅仅有一个函数是什么? 这个时候如果我们不使用

struct,仅仅使用函数指针又是什么? 我们发现,这样就退化为普通的函数指针的使用

了。

所以说,有的时候我觉得面向对象仅仅是一种形式,而不是一种技术。是一种观点,而

不是一种算法。但是,正如炭,石墨和钻石的关系一样,虽然分子式都是C,但是组成方法不

一样,表现就完全不一样了!

有的时候,我们经常被编程中琐碎的事情所烦恼,而偏离了重心,其实程序可进化的特

性是很重要的。有可能,第一次是不成功的,但是只要可进化,就可以发展。

4.进阶――类结构树,父类不是纯虚类的类

前面仅仅讲的是父类是纯虚结构的情况 (面向对象建议的是所有类的基类都是从纯虚类

开始的), 那么当类层次比较多的情况下,出现父类不是纯虚结构怎么办呢。嘿嘿,其实在C

中的实现比C++要简单多了。因为C中各个函数是分散的。

在这里使用宏定义是一个很好的办法:比如两个类Act1,ActByOther1“继承”Act1:

MyVirtualInterface* ActByOther1_CreatInterface()

{

index=FindValid() //对象池或者使用Malloc

if(index==-1) return NULL;

St[index].Foo1= ActByOther1_Foo1; // Act1_Foo1要在下面具体实现

St[index].Foo2= ActByOther1_Foo2;

St[index].Foo3= ActByOther1_Foo3;

Return &st [index];

}

#define ActByOther1_Foo1 Act1_Foo1 //这就是继承 嘿嘿

ActByOther1_Foo2(){} // 可以修改其实现

ActByOther1_DoByOther() {} //当然就可以添加新的实现咯

5.实例――可以参见H264的源码,其中NalTool就是这样的一个纯虚结构。

类模拟中使用了大量的函数指针,结构体等等,有必须对此进行性能分析,以便观察这样的

结构对程序的整体性能有什么程度的影响。

1.函数调用的开销

#define COUNTER XX

void testfunc()

{

int i,k=0;

for(i=0;i

}

在测试程序里面,我们使用的是一个测试函数,函数体内部可以通过改变YY的值来改变

函数的耗时。测试对比是 循环调用XX次函数,和循环XX次函数内部的YY循环。

结果发现,在YY足够小,X足够大的情况下,函数调用耗时成为了主要原因。所以当一个

“简单”功能需要“反复”调用的时候,将它编写为函数将会对性能有影响。这个时候可以使用

宏,或者inline关键字。

但是,实际上我设置XX=10000000(1千万)的时候,才出现ms级别的耗时,对于非实时

操作(UI等等),即使是很慢的cpu(嵌入式10M级别的),也只会在XX=10万的时候出现短

暂的函数调用耗时,所以实际上这个是可以忽略的。

2.普通函数调用和函数指针调用的开销

void (*tf)();

tf=testfunc;

测试程序修改为一个使用函数调用,一个使用函数指针调用。测试发现对时间基本没有

什么影响。(在第一次编写的时候,发现在函数调用出现耗时的情况下(XX=1亿),函数指

针的调用要慢(release版本),调用耗时350:500。后来才发现这个影响是由于将变量申请

为全局的原因,全局变量的访问要比局部变量慢很多)。

3.函数指针和指针结构访问的开销

struct a {

void (*tf)();

};

测试程序修改为使用结构的函数指针,测试发现对时间基本没有什么影响。其实使用结

构并不会产生影响,因为结构的访问是固定偏移量的。所以结构变量的访问和普通变量的访

问对于机器码来说是一样的。

测试结论:使用类模拟的办法对性能不会产生太大的影响。

0818b9ca8b590ca3270a3433284dd417.png

#include

0818b9ca8b590ca3270a3433284dd417.png#include 

0818b9ca8b590ca3270a3433284dd417.png

#define

VIRTUAL

0818b9ca8b590ca3270a3433284dd417.png

struct

vtable

/*虚函数表*/

0818b9ca8b590ca3270a3433284dd417.png

{

0818b9ca8b590ca3270a3433284dd417.pngint(*p_geti)(void*);/*虚函数指针*/0818b9ca8b590ca3270a3433284dd417.pngvoid(*p_print)();/*虚函数指针*/0818b9ca8b590ca3270a3433284dd417.png}

;

0818b9ca8b590ca3270a3433284dd417.png

0818b9ca8b590ca3270a3433284dd417.png

struct

shape

/*类shape*/

0818b9ca8b590ca3270a3433284dd417.png

{

0818b9ca8b590ca3270a3433284dd417.pngvoid*vptr;/*虚表指针 - 指向vtable*/0818b9ca8b590ca3270a3433284dd417.pnginti;

0818b9ca8b590ca3270a3433284dd417.png}

;

0818b9ca8b590ca3270a3433284dd417.png

0818b9ca8b590ca3270a3433284dd417.png

struct

circle

/*类circle*/

0818b9ca8b590ca3270a3433284dd417.png

{

0818b9ca8b590ca3270a3433284dd417.pngvoid*vptr;/*虚表指针 - 指向vtable*/0818b9ca8b590ca3270a3433284dd417.pnginti;

0818b9ca8b590ca3270a3433284dd417.png}

;

0818b9ca8b590ca3270a3433284dd417.png

struct

rectangle

/*类rectangle*/

0818b9ca8b590ca3270a3433284dd417.png

{

0818b9ca8b590ca3270a3433284dd417.pngvoid*vptr;/*虚表指针 - 指向vtable*/0818b9ca8b590ca3270a3433284dd417.pnginti;

0818b9ca8b590ca3270a3433284dd417.png}

;

0818b9ca8b590ca3270a3433284dd417.png

//

------------------------------------

0818b9ca8b590ca3270a3433284dd417.png

//

print() - 虚函数

0818b9ca8b590ca3270a3433284dd417.png

/*真正调用的函数,在其内部实现调用的多态*/

0818b9ca8b590ca3270a3433284dd417.pngVIRTUAL

void

print(

void

*

self)

/*参数是对象指针*/

0818b9ca8b590ca3270a3433284dd417.png

{

0818b9ca8b590ca3270a3433284dd417.pngconststructvtable*const*cp=self; 

0818b9ca8b590ca3270a3433284dd417.png    (*cp)->p_print();

0818b9ca8b590ca3270a3433284dd417.png}

0818b9ca8b590ca3270a3433284dd417.png

void

shape_print()

0818b9ca8b590ca3270a3433284dd417.png

{

0818b9ca8b590ca3270a3433284dd417.png    printf("this is a shape!\n");

0818b9ca8b590ca3270a3433284dd417.png}

0818b9ca8b590ca3270a3433284dd417.png

void

circle_print()

{

0818b9ca8b590ca3270a3433284dd417.png    printf("this is a circle!\n");

0818b9ca8b590ca3270a3433284dd417.png}

0818b9ca8b590ca3270a3433284dd417.png

void

rectangle_print()

0818b9ca8b590ca3270a3433284dd417.png

{

0818b9ca8b590ca3270a3433284dd417.png    printf("this is a rectangle!\n");

0818b9ca8b590ca3270a3433284dd417.png}

0818b9ca8b590ca3270a3433284dd417.png

//

------------------------------------------------------

0818b9ca8b590ca3270a3433284dd417.png

//

geti() - 虚函数

0818b9ca8b590ca3270a3433284dd417.png

VIRTUAL

int

geti(

void

*

self)

0818b9ca8b590ca3270a3433284dd417.png

{

0818b9ca8b590ca3270a3433284dd417.pngconststructvtable*const*cp=self; 

0818b9ca8b590ca3270a3433284dd417.pngreturn(*cp)->p_geti(self);/*这一行出问题*/0818b9ca8b590ca3270a3433284dd417.png}

0818b9ca8b590ca3270a3433284dd417.png

int

shape_geti(

struct

shape

*

self)

/*具体函数实现时,参数还要是其类型指针*/

0818b9ca8b590ca3270a3433284dd417.png

{

0818b9ca8b590ca3270a3433284dd417.pngreturnself->i;

0818b9ca8b590ca3270a3433284dd417.png}

0818b9ca8b590ca3270a3433284dd417.png

int

circle_geti(

struct

circle

*

self)

/*具体函数实现时,参数还要是其类型指针*/

0818b9ca8b590ca3270a3433284dd417.png

{

0818b9ca8b590ca3270a3433284dd417.pngreturnself->i;

0818b9ca8b590ca3270a3433284dd417.png}

0818b9ca8b590ca3270a3433284dd417.png

int

rectangle_geti(

struct

rectangle

*

self)

/*具体函数实现时,参数还要是其类型指针*/

0818b9ca8b590ca3270a3433284dd417.png

{

0818b9ca8b590ca3270a3433284dd417.pngreturnself->i;

0818b9ca8b590ca3270a3433284dd417.png}

0818b9ca8b590ca3270a3433284dd417.png

int

main(

int

argc,

char

*

argv[])

0818b9ca8b590ca3270a3433284dd417.png

{

0818b9ca8b590ca3270a3433284dd417.pngstructshape _shape;/*shape的对象_shape*/0818b9ca8b590ca3270a3433284dd417.pngstructcircle _circle;/*circle的对象_circle*/0818b9ca8b590ca3270a3433284dd417.pngstructrectangle _rectangle;/*rectangle的对象_rect*//*声名虚表*/0818b9ca8b590ca3270a3433284dd417.pngstructvtable shape_vtable;/*shape对象的vtable*/0818b9ca8b590ca3270a3433284dd417.pngstructvtable circle_vtable;/*circle对象的vtable*/0818b9ca8b590ca3270a3433284dd417.pngstructvtable rectangle_vtable;/*rectangle的虚表*//*给类分配虚表*/0818b9ca8b590ca3270a3433284dd417.png    _shape.vptr=&shape_vtable;/*将虚表挂上*/0818b9ca8b590ca3270a3433284dd417.png    _circle.vptr=&circle_vtable;/*将虚表挂上*/0818b9ca8b590ca3270a3433284dd417.png    _rectangle.vptr=&rectangle_vtable;/*将虚表挂上*/0818b9ca8b590ca3270a3433284dd417.png/*给虚表对应相应的函数*/0818b9ca8b590ca3270a3433284dd417.png    shape_vtable.p_print=shape_print;/*赋值相应的函数*/0818b9ca8b590ca3270a3433284dd417.png    circle_vtable.p_print=circle_print;/*赋值相应的函数*/0818b9ca8b590ca3270a3433284dd417.png    rectangle_vtable.p_print=rectangle_print;/*赋值相应的函数*/0818b9ca8b590ca3270a3433284dd417.png/*给虚表对应相应的函数*/0818b9ca8b590ca3270a3433284dd417.png    shape_vtable.p_geti=shape_geti;

0818b9ca8b590ca3270a3433284dd417.png    circle_vtable.p_geti=circle_geti; 

0818b9ca8b590ca3270a3433284dd417.png    rectangle_vtable.p_geti=rectangle_geti;/*动态联编实现多态*/0818b9ca8b590ca3270a3433284dd417.png/*因类型的不同而作出不同的反映*/0818b9ca8b590ca3270a3433284dd417.png    print(&_shape); print(&_circle); 

0818b9ca8b590ca3270a3433284dd417.png    print(&_rectangle); 

0818b9ca8b590ca3270a3433284dd417.png    _shape.i=5; 

0818b9ca8b590ca3270a3433284dd417.png    _circle.i=19; 

0818b9ca8b590ca3270a3433284dd417.png    _rectangle.i=1; 

0818b9ca8b590ca3270a3433284dd417.png/*动态联编实现多态*/0818b9ca8b590ca3270a3433284dd417.png/*因类型的不同而作出不同的反映*/0818b9ca8b590ca3270a3433284dd417.png    printf("_shape's i is : %d\n", geti(&_shape)); 

0818b9ca8b590ca3270a3433284dd417.png    printf("_circle's i is : %d\n", geti(&_circle)); 

0818b9ca8b590ca3270a3433284dd417.png    printf("_rectangle's i is : %d\n", geti(&_rectangle)); 

0818b9ca8b590ca3270a3433284dd417.png    system("PAUSE");

0818b9ca8b590ca3270a3433284dd417.pngreturn0;

0818b9ca8b590ca3270a3433284dd417.png}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值