使用C语言进行面向对象的开发[Hello,OOC World!]
准确说可能不太适合发在这个区,因为里面的东西似乎在单片机的开发上更为需要.不过这里似乎没有纯粹讨论这方面的区.希望有兴趣的人以及大牛们能够参与和使用,同时也提出宝贵的意见.
就是些关于如何用C模拟面向对象的机制的,
项目地址见→http://code.google.com/p/ooc-gcc/[开源协议:LGPL]
用纯C模拟OO的部分使用宏(但绝不是滥用),
不使用其它任何间接生成的机制,所有代码均保证纯GCC默认情况下直接零warning编译通过.
[测试环境为Linux以及win下MinGW套件,另有针对单片机使用的简化版,放在在下面的文档包中].
这个项目中最为核心的不是设计了三个宏→CLASS(用于类的设计),CTOR(类的构造函数),DTOR(类的析构函数)
同时在宏的设计方面也尽力做到如下几点
•易于记忆(比如核心宏只与有CLASS,CTOR,DTOR,并且所有宏从英文名称就能猜到其具体(或相关)作用)
•类设计开放(CLASS相关宏尽量简易)
•开放性(堆内存和栈内存的使用由用户根据需要决定,比如OOStd.h中的宏都是重定义的)
•对称性(就是有构造自然也有析构)
•方便调试与检测(那个OODbg是为了调试用的,具备纯C的try..catch模拟,堆内存日志分配记录等功能)
•父类构造析构的调用无需额外的代码
关于设计理念有几个草图,这里直接传上
http://cache.amobbs.com/bbs_upload782111/files_39/ourdev_639637W2F5SW.png
类设计 (原文件名:CLASS_DESIGN.png)
http://cache.amobbs.com/bbs_upload782111/files_39/ourdev_639638HBAJZF.png
析构与构造的模拟 (原文件名:CTOR_DTOR.png)
http://cache.amobbs.com/bbs_upload782111/files_39/ourdev_639639LN36EV.png
堆内存分配顺序介绍 (原文件名:MEMORY_ALLOC.png)
另外还有一份简单的说明文档,其中除了一篇还未完全完成的Tuorial中还包含了一些测试用的代码以及一些简化后的适合单片机的模拟OO的宏
这里也一并传在这里
Hello,OOC World!ourdev_639644R5K9ZK.rar(文件大小:305K) (原文件名:LearningOOC.rar)
注意目前这份口语化的文档尚未未完全完成,但涵盖了核心的设计思路,以及一些简化版的适合嵌入式用的宏.
完整的在PC或是高性能设备上使用的还是请参照http://code.google.com/p/ooc-gcc/中最新版的代码. 不错,
我目前正在用一个叫LWOOPC的东西,一组宏,可以实现封装、继承和多态 回复【1楼】syuanwang昙花公公
-----------------------------------------------------------------------
LS说的是那个高焕堂那本书中介绍的那个LWOOPC么?
那个有很多不足的地方,在我传的那份文档里有相应的对比和介绍. 这个必须要认真学!
未来是面向对象的世界! 这个要顶!
rt_thread也模拟面向对象的呀
也传点C模拟面向对象方面的
来源:互联网
C语言的面向对象技术
在面向对象的语言里面,出现了类的概念。这是编程思想的一种进化。所谓类:是对特定数据的特定操作的集合体。所以说类包含了两个范畴:数据和操作。而C语言中的struct仅仅是数据的集合。(liyuming1978@163.com)
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语言编程者能够享受面向对象编程的更多乐趣。我们仅仅面对的是浩瀚的“黑箱”,我们的工作是堆砌代码,而且如果要更改代码功能的时候,仅仅换一个黑箱就可以了。
而更大的目的是促使这样的黑箱的产生。或许有一天,一种效率很好,结构很好的语言将会出现。那个时候编程是不是就会象说话一样容易了呢?
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:实现虚结构,含有MyVirtualInterfacest; 有以下函数:
MyVirtualInterface* Act1_CreatInterface()
{
index=FindValid() //对象池或者使用Malloc !应该留在外面申请,实例化
if(index==-1) return NULL;
St.Foo1=Act1_Foo1; // Act1_Foo1要在下面具体实现
St.Foo2=Act1_Foo2;
St.Foo3=Act1_Foo3;
Return &st ;
}
子类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.Foo1= ActByOther1_Foo1; // Act1_Foo1要在下面具体实现
St.Foo2= ActByOther1_Foo2;
St.Foo3= ActByOther1_Foo3;
Return &st ;
}
#define ActByOther1_Foo1 Act1_Foo1//这就是继承 嘿嘿
ActByOther1_Foo2() {} //可以修改其实现
ActByOther1_DoByOther() {} //当然就可以添加新的实现咯
类模拟的性能分析
类模拟中使用了大量的函数指针,结构体等等,有必须对此进行性能分析,以便观察这样的结构对程序的整体性能有什么程度的影响。
1.函数调用的开销
#define COUNTER XX
void testfunc()
{
int i,k=0;
for(i=0;i<YY;i++){k++;}
}
在测试程序里面,我们使用的是一个测试函数,函数体内部可以通过改变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)();
};
测试程序修改为使用结构的函数指针,测试发现对时间基本没有什么影响。其实使用结构并不会产生影响,因为结构的访问是固定偏移量的。所以结构变量的访问和普通变量的访问对于机器码来说是一样的。
测试结论:使用类模拟的办法对性能不会产生太大的影响。 再来点
轻量级的面向对象C语言编程框架LW_OOPC
点击此处下载 ourdev_639660WFQ8L5.zip(文件大小:239K) (原文件名:lw_oopc v1.2.zip) 再来点,平时收藏的
来源:互联网
使用C语言实现面向对象的设计 一
一. 使用结构体实现类的功能。
1.1, 封装
#defineCLASSNAMEID100; // 类名对应的ID,在内存管理中使用
struct__classname
{
int classnameID; //该数值必须位于第一
int (*classname_getclassID) ( ); //得到类名对应的ID函数。该函数指针必须位于第一
int (* newclassname) (__classname* classname_this); //建构函数
//功能函数指针:
type (*classname_function1) (arguments, __classname* classname_this);
type (*classname_function2) (arguments, __classname* classname_this);
//type: 数据类型arguments:参数
//属 性变量。
type*classname_property1; //属 性。
type classname_property2;
__classname *classname_this; //每个类中必须包含的指向自身的指针
void (* delclassname) (__classname* this); //析 构函数
}
#typedef __classname classname;
#define CLASSNAME() \ //该宏将在继承中使用。
int classnameID; \
int (*newclassname)(classname*); \
int (*classname_function1)(int,classname*); \
int (*classname_function2)(int,classname*);\
int classname_property2;\
int* classname_property1; \
__classname *classname_this;\
void (* delclassname) (classname*);\
int (*classname_function3) (int,classname*);\
这些用结构体实现的类,我给它取名为结构类。
1.1. 1建构函数的实现
在结构类中的建构函数指针bool (*newclassname) (),其所对应的建构函数的函数名为该建构函数指针的大写形 式,即:
//建构函数执行初始化指针,属性等动 作。
intNEWCLASSNAME(classname* point)
{
point->classname_this= point;
point->classname_function1=Classname_function1;//function指针对应的函数,即为它的首大写
point->classname_property1=malloc(sizeof (type) ) ;
if(point->classname_property1==NULL)return 0; //失败
point->classname_getclassID=Classname_getclassID;
point->classname_property2=…;
point->classname_delclassname=Delclassname;
point->classnameID=CLASSNAMEID;
return 1; //成功
}
使用C语言实现面向对象的设计 二
1.1.2 功能函数的实现
在结构类中的功能函数指针,其所对应的函数名为该函数指针的大写形式。
typeClassname_function1 (….,classname* classname_this )
{
classname_this ->classname_property2=…….;
……..
}
1.1.3 析构函数的实现
在结构类中的析构函数指针,其所对应的函数名为该函数指针的大写形式
void Delclassname(classname* classname_this) //执行释放内存的操作。。
{ free(classname_this->classname_property1);
……..
}
1.1.4 得到类的ID函数的实现
void Classname_getclassID( )
{ return CLASSNAMEID;//返回类的ID;
}
1.2 继承
命名规则:子类名_父类名的前三个字符加上最后一个字符_父类名…
同 时从多个父类继承则取各个父类的前三个字符合在一起。
如果前三个字符有相同的,则取第1,2,4个字符,如果第4个还相同,取1,2,5,如果第5个还想同,取1,2,6,依此类推,直到取到不相同的。
例如:从baseclass 继承,派生出childclass,则可以命名为childclass_bass;
如 果从childclass_bass派生 boyclass,则 可以命名为boyclass_chis_bass;
如 果同时从baseclass, childclass_bass, boyclass_chis_bass派生johnclass,则可以命名为johnclass_baschiboy;
有结构类classname,从该类继承一个子类childclass.则该子类可以这样实现
使用C语言实现面向对象的设计 三
#define CHILDCLASS_CLAEID 101
struct __childclass_clae
{
int childclass_claeID; //该数值必须位于第一
int (*childclass_getclassID) ( ) ; //得到类的ID函数,该函数指针必须位于第一
CLASSNAME( ); //从结构类classname继承。
int (* newchildclass_bass) (__childclass_clae * childclass_clae_this); //建构函数
//功能函数指针:
type (*__childclass_clae _function3) (arguments, __childclass_clae * childclass_clae_this);
type (*__childclass_clae_function4) (arguments, __childclass_clae * childclass_clae_this);
//type: 数据类型arguments:参数
//属 性变量。
type*__childclass_clae _property1; //属 性。
type __childclass_clae _property2;
__childclass_clae *childclass_clae_this;
//每个类中必须包含的指向自身的指 针
void (* delchildclass_clae) (__childclass_clae * childclass_clae_this); //析 构函数
}
typedef__childclass_claechildclass_clae;
int Newchildclass_clae(childclass_clae * point)
{
//对父类classname 进行初始化。
Point->classnameID=CLASSNAMEID;
point->classname_this=(classname*) (&(point->classnameID));
if( ! INITIAL ( point->classname_this ); return 0;
//INITIAL在后面的内存管理2.3中有定义。
//对自身进行初始化
point->childclass_clae_property1=(type*)malloc sizeof(type));
point->childclass_clae_function3=Childclass_clae_function3;
point->childclass_clae_function4=Childclass_clae_function4;
Point->delchidclass_clae=Delchildclass_clae;
………
}
void Delchildclass_clae(childclass_clae * childclass_clae)
{
//释放父类的内存
(*childclass_clae ->delclassname)(childclass_clae->classname_this)
//释放自身的内存
free(childclass_clae->childclass_cale_property1);
…….
}
#define CHILDCLASS_CLAE() \ //该宏将在继承中使用
int childclass_claeID; \
int (*childclass_getclassID) ( ) ;\
CLASSNAME( );\ //从 结构类classname继承。
int (* newchildclass_bass) (__childclass_clae * childclass_clae_this);\
type(*__childclass_clae_function3)(arguments,__childclass_clae*\
childclass_clae_this);\
type (*__childclass_clae_function4) (arguments, __childclass_clae *\ childclass_clae_this);\
type*__childclass_clae _property1; \
type __childclass_clae _property2; \
__childclass_clae *childclass_clae_this;\
void (* delchildclass_clae) (__childclass_clae * childclass_clae_this);\
使用C语言实现面向对象的设计 四
1. 3多态。
在C语言中的不可能实现真正的多态,可以依靠在函数名上的变化,也 可以通过无参数指针指向有参数函数来实现。
例如,有这个函数intfunction(int ,char*) , 可以将之命名为functionicp, i和cp 表示参数为int,char*.依此类推。
function(int,int ,int),命名为functioniii;
所以声明函数就变成如下形式
intclassname_functioniii (int,int ,int );
intclassname_functionicp (int ,char*);
intclassname_functioniicp (int,int,char*);
二. 内存管理
2. 1建立内存分配的全局函数。
void* NEW (int CLASSNAMEID)
{
void* tem;
switch(CLASSNAMEID)
{
case CLASSA:
{
tem=malloc( sizeof (classa) );
(classa*)tem->newclassa=Newclassa;
classa* temp=tem;
if( !( (*(temp->newclassa)) ( (classa*) tem ) ) //调用建构函数
{
free(tem);
return NULL;
}
return tem;
}
case CLASSB:
{
tem=malloc ( sizeof(classb) );
(classb*)tem->newclassa=Newclassa;
classb temp=tem;
if( ! (* (temp->newclassb)) ((classa*) tem ) )
{
free(tem);
return NULL;
}
return tem;
}
case …..
….
}
}
使用C语言实现面向对象的设计 五
2,2建立内存释放的全局函数。
voidDELETE(void* object)
{
if ( object == NULL )return;
switch(*(int* ) object ) //调用classnameID
{
case CLASSA:
{
Delclassa ( ((classa*)object)->delclassa );
//调用析构函数
break;
}
case CLASSB:
{
Delclassb((( classb*)object)->classa_this);
break;
}
case…..
……..
}
free(object); //释放对象
object=NULL;
}
2.3 初始化变量的全局函数
void INITIAL(void* object)
{
if(object == NULL) return ;
switch(*(int*)object)
{
case CLASSA:
{
((classa*)object)->newclassa=Newclassa;
if(!Newclassa((classa*)object)) return 0;
break;
}
case CLASSB:
{
((classb*)object)->newclassb=Newclassb;
if(!Newclassb((class*)object)) return 0;
break;
}
case…….
………..
}
}
使用C语言实现面向对象的设计 六
三 ,示例。
下面有一个完整的例子。在vc.net,win2000 server的环境下编译通过,运行正确。
///////////////////////////////////////////////////////////////////////////////////////////////
//testchild.cpp.
//
//this code is compiled in Visual c++ .net 2002 .
//run in windows2000 server.
//
//made in 2004.8.8 by Feng
/////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
int INITIAL(void* object);
void* NEW(int CLASSID);
voidDELETE(void* object);
#defineCLASSNAMEID100 // 类名对应的ID,在内存管理中使用
struct__classname
{
int classnameID;
int (* classname_getclassID)(void);
int (* newclassname) (__classname* classname_this);
int (*classname_function1) (int i);
int (*classname_function2) (__classname* classname_this);
int*classname_property1;
int classname_property2;
__classname *classname_this;
void (* delclassname) (__classname* classname_this);
} ;
typedef __classname classname;
int Classname_getclassID()
{
return CLASSNAMEID;
}
int Classname_function1(int i)
{
printf("%d",i);
return 1;
}
int Classname_function2(classname* classname_this)
{
printf("%d",classname_this->classname_property2);
return 1;
}
void Delclassname(classname* classname_this)
{
free((classname_this->classname_property1));
printf("free is ok");
}
int Newclassname(classname* point)
{
point->classname_this=point;
point->classname_function1=Classname_function1;
point->classname_function2=Classname_function2;
point->classname_property2=100;
point->classname_property1=(int*)malloc(sizeof(int)*100);
if(!point->classname_property1) return 0;
point->classname_getclassID=Classname_getclassID;
point->delclassname=Delclassname;
point->classnameID=CLASSNAMEID;
return 1;
}
使用C语言实现面向对象的设计 七
#define CLASSNAME() \
int classnameID;\
int (*classname_getclassID)();\
int (*newclassname)(classname* point);\
int (*classname_function1)(int i);\
int (*classname_function2)(classname* point);\
int classname_property2;\
int* classname_property1;\
__classname* classname_this;\
void (*delclassname)(classname* point);\
#define CHILDID 102
struct __child
{
int childID;
int(*child_getclassID)();
CLASSNAME();
__child* child_this;
int (*newchild)(__child* child_this);
void (*delchild)(__child* child_this);
};
typedef __child child;
int Child_getclassID()
{
return CHILDID;
}
void Delchild(child* point)
{
(*(point->delclassname))(point->classname_this);
printf("free child is ok");
}
int Newchild(child* point)
{
point->classnameID=CLASSNAMEID;
point->classname_this=(classname*) ( &( point->classnameID ));
if(!INITIAL(point->classname_this)) return 0;
point->delchild=Delchild;
point->childID=102;
point->child_getclassID=Child_getclassID;
return 1;
}
#define CHILD()\
int childID;\
int(*child_getclassID)();\
CLASSNAME();\
__child* child_this;\
int (*newchild)(__child* child_this);\
int (*delchild)(__child* child_this);\
使用C语言实现面向对象的设计 八
void* NEW (int CLASSID)
{
void* tem;
switch(CLASSID)
{
case CLASSNAMEID:
{
tem=malloc(sizeof(classname));
if(!tem) return NULL;
((classname*)tem)->newclassname=Newclassname;
classname* temp=(classname*)tem;
if(!(*(temp->newclassname))((classname*)tem) )
{
free(tem);
return NULL;
}
return tem;
}
case CHILDID:
{
tem=malloc(sizeof(child));
if(!tem) return NULL;
((child*)tem)->newchild=Newchild;
child* temp=(child*)tem;
if(!(*(temp->newchild))((child*)tem) )
{
free(tem);
return NULL;
}
return tem;
}
default:return NULL;
}
}
void DELETE (void* object)
{
if(object == NULL) return ;
switch(*(int*)object)
{
case CLASSNAMEID:
{
Delclassname((classname*)object);
break;
}
case CHILDID:
{
Delchild((child*)object);
break;
}
default:break;
}
free(object);
object=NULL;
} 搞得这么复杂,为什么不直接用C++,至少C++还有标准模板库.如果因为C++比C运行时尺寸大,不如干脆用C得了. 好东西! 回复【5楼】wenxusun番薯猪猪
-----------------------------------------------------------------------
那个lw_oopc 好像是一位同学在征得高焕堂先生同意后一个修改兼加强版.
但是设计的依旧很一般,
如果你说轻量级的,我在传上的那份文档中有一个很轻量级,很对称的实现(有构造也有析构,且只有30行左右的代码,当然功能也很清凉).
个人认为比lw_oopc的设计要好上不少. 回复【7楼】zf8848
-----------------------------------------------------------------------
我个人不是很喜欢C++,原因是其语法冗杂且编译器隐藏了太多的我们必须明确知道的细节.
如果要用高级点的方便的语言,我个人比较喜欢python的设计和实现,功能强大,简洁明确.
另外这个设计的初衷有一部分时针对单片机使用的,我在传上的那份文档中有一个很轻量级,很对称的实现(有构造也有析构,且只有30行左右的代码,当然功能也很轻量),并且详细的分析了为什么这样实现.
还有有些东西是需要我们自己动手实现的,有些领域只有C才适合...总之原因有很多...
至于从好用的方面来说,C++的易用性与python,ruby这些相差甚远,当然其性能是好的,但是追求性能的东西很多也可以用C,就算java现在的性能也不差太多... 【9楼】 pingf0
我是刚接触这方面的,没有你见解那么深
做过一些项目,感到面向过程的思想编程有点复杂。看了一段话感触很深:
我在网上看见过一篇文章讲述了类似的思想(据说C++编程思想上有更加详细的解说,可惜我没空看这个了,如果有知道的人说一说吧)。但是就象C++之父说的:“C++和C是两种语言”。所以不要被他们在语法上的类似就混淆使用,那样有可能会导致一些不可预料的事情发生。
其实我很同意这样的观点,本文的目的也不是想用C模拟C++,用一个语言去模拟另外一个语言是完全没有意义的。我的目的是想解决C语言中,整体框架结构过于分散、以及数据和函数脱节的问题。
C语言的一大问题是结构松散,虽然现在好的大型程序都基本上按照一个功能一个文件的设计方式,但是无法做到更小的颗粒化――原因就在于它的数据和函数的脱节。类和普通的函数集合的最大区别就在于这里。类可以实例化,这样相同的函数就可以对应不同的实例化类的变量。
于是尝试用这种方法编程 MARK OOPC 回复【11楼】wenxusun番薯猪猪
-----------------------------------------------------------------------
说起C++,据说早年也有一段黑历史,就是其设计之初也是走的这个路线,从简单加强C本身来开始的,也有一些通过宏来加强的尝试....
这个项目最初的目的是让C看上去有更好的封装形式,看上去更易懂且更好使用,绝非是模拟某种语言.
"它的数据和函数的脱节"这本身不是什么大问题,我们可以用结构体封装函数指针和普通成员,这也使得C有了面向对象的编程风格.反倒是C++由编译器将其放在一起牺牲了一些自由度(这里的一起指使用时在一起,不是内存分布上的一起).
另外说一下面向对象的C,其实有两部分,一部分是面向对象的编程风格,另一部分是如何简化因使用面向对象风格而带来的冗余代码.
前一部分的一个问题是面向对象的风格具体到细节上也是千差万别,后一部分的问题是到底采用什么方式来掩藏冗余的代码.后一部分常规的解决方案有两种一种用独立的预处理程序来改造代码,另一种就是用C本身支持的宏的特性.
另外关于算法,OO思想,FP思想,设计模式这些编程领域的玩意儿,个人的看法是
不要不加思索的膜拜算法的地位,不要迷信OO能解决一切问题,不要因为FP的冷门而轻视它,不要为了模式而模式.
另外这方面因为本身不是很难,所以没什么入门不入门之说,只要有OO的思想上手都很容易,也希望 番薯猪猪 能在看了那份简单文档后提出宝贵的意见. pingf0的文章深入浅出,赞一个!期代完整版
面向对象还有一个多态,好像未见提及 LZ的东西大致扫了一下,的确挺好的,应该比LWOOPC好一些。
不过在用了LWOOPC之后,接受了OOP的理念后,已经决定转到C++了,非原生OOP语言用起来的确是不顺畅
目前由于项目进度和现存代码的问题,用C了结算了。 回复【14楼】wenxusun番薯猪猪
-----------------------------------------------------------------------
"多态"其实我在文章中简单说了一句,但个人的理解这并不是OO的核心,个人理解的OO的核心还是如何去封装数据,让其成为一个对象,用C模拟OO当然也是从最核心的开始.
很多人喜欢多态这个更多的原因是这个特性比较省事儿,真想省事儿还是用python或者ruby之类的吧.
多态在C语言中的模拟更多是体现在OOC要完成的两部分分内容的前一部分,也就是OO的编码风格上,而要从简化这一部分代码上来说最好还是用代码生成的方式会好些,用宏来简化就比较困难了.
不过对于抽象的数据类型,如果只传指针,可以用void*来转一下,多少有点感觉.
还有就是用宏来重定义名称(当用不同类型的时候,在取消原宏的定义并充定义一下),<C和指针>以及<C接口和实现>也有相关的介绍.不过个人的观点是这样写出来的代码不够neat.
另外GCC还有一些特性,比如局部函数,在配合一定设计模式时也会让代码有点多态的"感觉",但这也只是让C仅仅有些多态的感觉而已.
其实OO里面除了基本封装之外另一重点是类型系统的概念,个人的理解这个可以通过类库来实现.比如那个GObeject,主要就是提供了一个类型系统.当然不排除以后有时间去写一些OO风格类型系统库的想法,只是那个工作量会很大. 回复【15楼】syuanwang昙花公公
-----------------------------------------------------------------------
C++本身编译器隐藏了太多细节,同时它的的OO机制也不够原生的,而且语法很冗杂,标准不够统一,方言特性不统一,另外目前的语法已经够繁多的了,C++0x又要引入一堆东西.
OOP用起来比较舒服的是python和ruby.不过兼顾性能和开发效率的话可以看下lua.
用C去模拟OO的一个好处是,一切尽在掌握. C是过程语言,C++是面向对象的
为什么一定要用一个过程语言去模拟成对象呢?
感觉没多大用处,最多在某个实际项目中用用而已吧~ 回复【18楼】380121850六月
-----------------------------------------------------------------------
C++的面向对象也不纯的,但是OO只是思想而已,即使是C也能写出OO的代码
绝对不是一定要用过程语言去模拟对象.
只是C语言在代码量比较大时(比如2万行以上的纯C程序)难于维护,
通过"对象化/模块化"的编程风格在一定程度上可以解决此问题,
关于这方面的好处<C接口和实现>一书讲的比较全面. 回复【18楼】380121850六月
-----------------------------------------------------------------------
还有就是
高焕堂的<UML+OOPC嵌入式C语言开发精讲>,李先静的<系统程序员成长计划>等书中都介绍了对于C语言开发使用"对象化/模块化"编程风格的好处. 手机党mark 回复【14楼】wenxusun番薯猪猪
-----------------------------------------------------------------------
刚刚又找到了高焕堂的讲他那个lw_oopc的书,发现里面有所谓的多态,很是简单的,只是一个投币机的例子而已,而其实现方式也是在中间加了一"层"而已,要做到这点只是设计模式上的问题.
既然是模式上的模拟,用这个肯定可以啦!只是目前还没有写进文档.
我没有明确在手册里说这些以及上面的回复的主要意思是想用宏去简化这种所谓"多态"而产生的冗余代码是很难做到的. 面向对象已经过时了,未来的“函数式编程”的天下!!!!!!!!!
面向对象已经过时了,未来的“函数式编程”的天下!!!!!!!!!
面向对象已经过时了,未来的“函数式编程”的天下!!!!!!!!! 回复【23楼】ufbycd
-----------------------------------------------------------------------
我说未来是"过程式"的你信不?
因为到最后都是汇编啦,机器指令码.
争论这个没意义的.
不过话说现在Haskell也渐渐火起来了! TO:pingf0
不知你看过rt_thread os的源码或文档介绍没有。都是用C模拟面向对象写的。我贴个他说的多态出look look
http://cache.amobbs.com/bbs_upload782111/files_39/ourdev_639768XPLHLI.JPG
(原文件名:000.JPG)
http://cache.amobbs.com/bbs_upload782111/files_39/ourdev_639769UR8ZGR.JPG
(原文件名:001.JPG) mk 不想过多争论使用什么语言。我只知道,未来很长一段时间我还得使用C,因为受资限制一些系统很难使用其他高级语言做开发。所以我还是学好C、灵活使用C 回复【25楼】wenxusun番薯猪猪
-----------------------------------------------------------------------
rt_thread os的那个和高焕堂介绍的类似,更多的只是一种编码风格上的多态.但他们并没有通过宏或代码生成去解决额外的代码劳动.
但这更多是模式层面的问题,因为这种多态你还是要额外手动区增加一个抽象类,并手动重新设置一下那个函数指针!
这是模拟OO的最基本的方法,我这个自然也可以这样,我的文档中也有类似的例子.
现在重点要解决的是简化oo风格的编码规范,通过一定的宏简化额外的代码劳动.这才是我写这些的主要意义所在. 我感觉,C是专门为单片机或ARM而生的,因为这些东西强调的都是过程。。。
操作系统上的编程,一般不用C了吧,现在 好东西,认真学习! 学习学习 rt-thread 回复【楼主位】pingf0
-----------------------------------------------------------------------
主要说一下目前文档中的例子涉及STATIC部分的可能会有内存释放上的bug,完整版已经修正此错误[具体见项目主页]
另外下面先贴一点整理好的文档
项目地址 OOC-GCC : http://code.google.com/p/ooc-gcc/源码以LGPL发布,文档为GPL3
转载请注明出处
1.1 什么是OOC
OOC即Object Oritened C,当然也有些人称之为OOPC,多出来的P指Programming.
而OO指面向对象,当然有OB(Object Based)一说,就是基于对象,在这两个名词之间不多
啰嗦,一句话概括的化,OO比OB更“纯”一些,尽管这样概括并不够准确.
本手册所说的面向对象的C主要涉及两个方面,风格与简化!
OOC涉及的两个主要方面
1. 采用何种面向对象的编码风格
2. 如何简化因为使用某种编码风格而产生的额外代码
第一方面不同的人会有不同的见解,虽然千差万别,但却大同小异. 第二方面主要的
实现机制还有两种,一种是通过独立的预处理程序来生成额外的代码,另一种是用C语言本
身支持的宏特性来简化. 前者可以让代码获得更多的特性,后者可能无法很好的简化部
分因为OO风格而产生的冗余代码,但是使用起来却更加方便.
当然准确一些的话OOC还应该有一个较为完整的类型系统,比如GObject库这方面做
的比较全,但是个人认为其前两方面做得不怎么样,所以用纯C开发起来并不方便.
本手册主要是围绕一个名为OOC-GCC的小项目展开的,自然介绍的是其中采用的OO风
格,而这个项目本身针对更多是纯C的开发, 并且在使用上尽力做到简易,所以采用了宏来
完成“偷懒”的工作.
1.2 面向对象中常涉及的概念
1. 类,成员,接口与方法
2. 包,模块,命名空间,访问权限
3. 重载,重写,继承,多态,泛型,反射与自省
上面写的东西可能不全面,但即使这些展开说也多了去了,而且具体到某些细节我
的理解也不一定全都正确. 所以一来为了简洁,二来为了不去一不小心误人子弟.这些东
西我争取用一两句话来介绍.而因为本手册是针对C来使用的,也会给出一些在C语言里的
对应.
类及类成员是啥我就不多说了,就像C中的结构与结构成员.接口与方法到C里面都指
函数.tmd也不知哪个龟孙开始提出的OO,整出这么多概念来. C语言常用结构体来模拟
类,自然普通结构成员就对应类成员,而类的方法采用函数指针来模拟,个人的理解是这种
指针的模拟都对应接口或是虚方法. 当我们把某个函数指针指向具体的函数,就相当于
真正的实现了某个接口或是将虚方法化虚为实.
C语言里没有什么包,命名空间等这类东西,不过模块化的C一般就是采用独立文件
的编写方式. 常把一个模块放在一个.c和一个.h文件中,不同的模块经过编译产生了
众多的.o文件,再通过链接将各个.o统一起来. 关于访问权限,一般的OO语言常涉
及public,private,protected这些,主要是限制是类中访问与类外访问以及继承时的权
限. C语言可没这么多条条框框,大体上除了局部括号内受限以及static关键字限定某些
文件内访问.其它的都很自由,你自己掌控一切.
关于继承,我见过两种描述,当然个人觉得这两种说法并不冲突,只是侧重点不太
相同. 一种感觉重在描述“数据抉择”这一块,就是如何去使用数据. 其说法是子类
使用父类的方法这是继承,而子类实现父类的接口在“向上转型”成父类再来使用以获得
不同的实现这是多态(的一种体现). 另一种关于继承的解释重在“数据封装”,就是继承
就是子类包含父类的东西,从本之上说是一种包含关系.
个人比较喜欢从“数据封装”方面来阐述继承,因为这更和字面意思,和现实生活也
更加对应.比如儿子继承母亲的某些性格特征, 继承遗产等等,侧重的也是“数据封装”.
当然现实中的继承远比程序中的复杂,而且很多现实继承是那种父类大,子类小,子类局部
继承的情况. 而程序中的继承基本都是由小到大的模式,这就要求我们进一步分析并
采用一些模式来完成数据的抽象.
从“数据封装”方面来看代码中的继承,大体也有两种.一种是一般成员继承, 这
种就是前面从“数据抉择”上的通过子类使用父类,另一种是接口等的继承,这种具体
实现是在子类中完成的, 但使用时以父类的形式使用,如前所述“数据抉择”上通过父类
使用子类的情形,一个父类定义接口,有很多子类的不同实现,这就体现了多态这一概念.
用C语言来完成“数据封装”上的继承,一种常规方式是子类结构体包含父类结构体
的形式.并且把父类结构体放在子类结构体的首部, 这样做的一个好处是当我们在堆上分
配一个对象,只需强制转换其指针到父类指针形态,就可以按照父类来使用了,当然这种
使用本身看上去是通过父类使用子类,也就是多态(的一种).
接下来说一下重载重写等相关的定义,这一块我直接用英文了(因为我有些整体相当不
错的书的中译这一块翻译上总有差异), 就是overload,override,overwrite(还有人
有时还会用到一个词overlap).关于这一块我想多说几句,就是关于这几个词不同语言上
也可能有一些细微的差异. 而且很多所谓的OO语言对这一块虽然支持,但并不完美,尤其
体现在overload运算符以及返回值的处理的支持上. 简单说overload是允许某类中名
称相同但参数等不同的方法的存在,这需要编译器或解释器能够通过传入参数等进行判断
进而确定到底是用哪个方法. override和overwrite都是子类中有和父类重名的方法,
不同的是overwrite会抹杀掉父类的方法,而override只是隐藏了父类的方法. 从C语言
来说,因为方法都是用函数指针来模拟,所以这些都可以模拟,但是都不是自动的,需要相
当的额外代码来辅助,甚至是需要构件一个抽象类作为中间层来模拟.
和overXXXX系的对比着可以说一下的就是泛型,针对的是几个不同参数的方法,让编
译器根据参数等来判断.泛型通常是只写一个方法,但是参数采用抽象的形式, 编译器通
过类型推衍等来判断具体是什么数据(当然这么概括泛型是为了简便,可能不太全面).
最后说一下反射(reflection)和自省(introspection),这是更加动态的机制,可能
需要类型系统等的支持.这两个概念有些人分不清,也有些人干脆把他们揉在一起来理解,
不过这也不影响,只要知道其动态特性怎么用就好. 简单的说反射式通过一个动态的字
符串,获得相应的类或方法等.自省则是通过一个已有的但未知类型的实例直接获得其相
关信息或是直接使用.C99好像支持了__TYPEOF__(gnu套件一般用小写的typeof),有点
自省的意思了. 但是去模拟反射却不容易,需要额外的数据结构以及约定好的类型系统
库才行(个人觉得).还有要说的是反射和自省看似美丽但是用起来代价也是不小的,比如
额外引入的数据处理对性能影响比较大, 很多时候使用这些机制会将代码变得冗长难懂,
类型不安全等.
广义上讲多态就是只从多种数据中选择需要的适合的来用,从这个角度来说它与前面
说的“两大部分”中“数据抉择”部分直接对应了.这样理解的话,它就包含了前面说
的overload,泛型,反射,自省等等. 而广义上的继承则也占据了“数据封装”中的相当
一部分.因此某种意义上说广义的继承和广义的多态就是OO的核心(不要动不动就下定
义,所以我加了个限定词“某种意义上”)
用C去模拟OO,其实无论“数据封装”或是“数据抉择”哪一部分都能模拟,只是模拟
的时候额外的代码量也是相当可观的. 而单从用宏来简化这些额外代码上,主要能简化
的就是“数据封装”的部分,“数据抉择”部分如果想要简化最好还是通过间接代码生成
的方式. 也因此OOC-GCC中的宏主要是在解决“数据封装”方面的问题,对于“数据抉
择”方面的OO模拟还是要写不少额外的代码(最近测试了一些和完全对应的C++代码
编译出来大小相差不大,相对还小些,特别使用到堆内存的时候).
注意
上面的概念可能有不少地方不够准确或是存在错误, 如果有所发现, 还请联系
我pingf0@gmail.com. 另外后面的章节尽量不再解释和OO相关的抽象概念,如有需要,
请参阅此节. MARK mark ufbycd 发表于 2011-5-14 15:09 static/image/common/back.gif
面向对象已经过时了,未来的“函数式编程”的天下!!!!!!!!!
面向对象已经过时了,未来的“函数式 ...
学Python 的时候见识到函数式编程的强大。
函数式编程能应用在嵌入式编程上吗?怎么去实现?
受益匪浅{:handshake:} 标记。。。。。 曾经一段时间想把mcu用oop的方式去写,而且也严格按照oop的定义去做,也研究了c语言如何实现oop,但到最后都彻底放弃了,mcu不适合严格意义的oop,简化的可能还行。 顶一下,觉得不错。
访问不了 http://code.google.com/p/ooc-gcc/
不知道楼主这几年有没有更新。 lovecxm 发表于 2014-10-4 14:33
曾经一段时间想把mcu用oop的方式去写,而且也严格按照oop的定义去做,也研究了c语言如何实现oop,但到最后 ...
没意义的,还不如仿C#的方式写代码。。
好复杂啊 没事去看看RTThread的源码,就知道你这些,都是没用的形式主义。 为什么要蹩脚的 搞C++那一套,
这么完出来,估计代码比C++的效率还低。
C++的优越,在于编译器帮你管理了很多工作,比如类方法。
面向对象思想很重要,当肯定做不到c++那样方便。
其实c++肯定比c先进很多,现在,包括操作系统都可以用c++来编写。有时候为了使用方便,c语言不得不牺牲资源来实现某种功能。 谢谢分享
页:
[1]