




已阅读5页,还剩3页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
GObject?GObject是一个基于C语言的面向对象的实现。它为C语言提供了一整套面向对象的接口,其特性有封装、继承、重载、类定义、接口、信号、引用计数等。它与GLib一并为GTK+的两大基石。C vs C+C和面向对象,这不就是C+么?为什么要搞出另一套东西,而不直接使用C+呢?关于C与C+之争是一个大坑。Linux之父Linus就是力挺C而批判C+的。讨厌C+的人似乎认为C+过于复杂,内部机制陷阱过多等等。自己的经历不多,用C+也很少,达不到大牛们的境界,如果让我给个非要用C而不用C+的理由,我也给不出一个有说服力的。为什么研究GObject最原始的动力是,我在使用GTK+进行开发,而GObject是GTK+的基石。如果基础不牢,上层一定不会稳,因此很有必要把GObject给过一遍。知道了它的内部,才知道该如何使用它,明白它的机制与原理,做到心中有数。但是研究GObject能带来更多。由于C里没有任何面向对象机制,因此GObject把这些机制全部实现了一遍。从中可以看到一些机制的实现原理,从而对面向对象有更多的理性了解。第一步:封装面向对象的最基本需求就是封装。所谓封装,按我的理解,就是将一系列相关数据,及对这些数据有关的操作,有序的组织在一个结构中。一个圆形有x坐标、y坐标、半径三个参数,我们可以用这三个变量表示一个圆:?1double x, y, radius;这没什么问题。现在多了一个圆,我们又要用三个变量:?1double x1, y1, radius1;当我们有很多个圆的时候,可能要用到数组:?1double x100, y100, radius100;问题在哪?x、y和radius是相互独立的。我完全可以定义100个x,200个y,150个radius。如果不只有圆,还有矩形,那么矩形的坐标叫什么呢?xx、yy?等你写了一堆代码之后回来看,到底x和y是圆的坐标,还是xx和yy是圆的坐标?所以有了struct。一个struct对数据进行了很自然的封装:?123struct Circle double x, y, radius;好了,现在我们有了Circle这个类型。这个类型将圆的三个参数封装到了一起,从现在开始它们就是一个整体了。我可以很自然的声明一个圆,而不是它的三个参数:?1struct Circle c;我们也不用担心x、y、z的数量不等了,更不用担心坐标和矩形坐标命名冲突它们定义在Rectangle这个struct里呢:)。事情还没有完。有了圆这个类型,那么对圆的操作呢?假设一个圆的操作之一为移动(move)。我们可以定义如下函数:?1234void circle_move (struct Circle *self, double x, double y) self-x = x;self-y = y;我们输入一个圆的指针,以及新的x、y坐标,移动操作帮助我们把指定的圆移动到新的坐标上。注意第一个参数self,是不是有点眼熟?它就是C+里的this。记得学C+时很多同学对this理解相当困难,如果看这个self就不难理解了:self就是我们要操作的那个变量,它是一个指针。C+在对象方法调用时省略了这个参数,它可以被编译器自动设置。在C里面,这个工作要我们自己做。因此移动一个圆要这么调用:?12struct Circle cir;circle_move (&cir, 10.0, 5.0);注意self是个指针,因为C里没有引用,所以我们只能使用指针来达到传递一个对象,而不是传递它的复制品的效果。这个方法不就是普通的函数调用嘛,根本就没把操作给封装呀。好,现给一个看起来像C+中的方法:?12345678struct Circle double x, y, radius;void (*move) (struct Circle *self, double x, double y);.struct Circle cir;cir.move = circle_move; /注意这句,是将已经定义好的函数赋值给一个函数指针,cir.movecir.move (&cir, 10.0, 5.0);通过函数指针,可以让move调用看起来更像C+了。但是,有两个不爽的地方。其一,要显式地将circle_move函数赋值给move函数指针,如果有5个圆,那就要5行指定的代码(除非用数组+循环)。更为严重的是我们可以为不同的变量指定不同move操作。其二,调用时依然要显示地指定self(&cir,就是实例化的对象本身),这带来的一个后果是,我们完全可以调用cir1的move,但是传入的是cir2的指针。对于第一点,可以使用类结构+初始化函数来解决。对于第二点,C语言是没法避免显示的传入self指针(如果可以的话请告诉我)。因此这种写法只是“像”C+而已,没啥实际的好处。不过在之后我们会看到,GObject会在类结构中使用函数指针来表示对象的操作。GObject的对象系统是一个建立在GLib基础上的,用C语言完成的,提供了一种灵活的、可扩展的、并容易映射(到其它语言)的面向对象的C语言框架。在GLib和GTK+中使用C的语法模拟CPP的行为,实现继承。 GLib中最有特色的是它的对象系统:GObject System,它是以Gtype为基础而实现的一套单根继承的C语言的面向对象的框架。GType是GLib运行时类型认证和管理系统。GType API是GObject的基础系统,所以理解GType是理解GObject的关键。Gtype提供了注册和管理所有基本数据类型、用户定义对象和界面类型的技术实现。(注意:在运用任一GType和GObject函数之前必需运行g_type_init()函数来初始化类型系统)。 在GObject系统中,对象由三个部分组成:(1)对象的ID标识(唯一,无符号长整型,所有此类对象共同的标识);(2)对象的类结构(唯一,结构型,由对象的所有实例共同拥有);(3)对象的实例(多个,结构型,对象的具体实现)。二、详解1、继承GObject的对象Boy(父类是GObject)(1)头文件boy.h:htmlview plaincopy1. #ifndef_BOY_H_2. #define_BOY_H_3. #include4. /*BOY_TYPE宏封装了boy_get_type函数,可以直接取得并替代Boy对象的ID标识;5. #defineBOY_TYPE(boy_get_type()6. #defineBOY(obj)(G_TYPE_CHECK_INSTANCE_CAST(obj),BOY_TYPE,Boy)7. *BOY(obj)宏是G_TYPE_CHECK_INSTANCE_CAST宏的再一次封装,目的是将一个Gobject对象强制转换为Boy对象8.9. typedefstruct_BoyBoy;10. typedefstruct_BoyClassBoyClass;11. /*结构类型_Boy是Boy对象的实例,就是说我们每创建一个Boy对象,也就同时创建了一个Boy结构。12. *Boy对象中的parent表示此对象的父类,GObject系统中所有对象的共同的根都是GObject类,所以这是必须的;13. *其它的成员可以是公共的,这里包括表示年龄的age,表示名字的name和表示方法的函数指针cry,14. *外部代码可以操作或引用它们。15. */16. struct_Boy17. 18. GObjectparent;19. gintage;20. gchar*name;21. void(*cry)(void);22. ;23. /*结构类型_BoyClass是Boy对象的类结构,它是所有Boy对象实例所共有的。24. *BoyClass中的parent_class是GObjectClass,同GObject是所有对象的共有的根一样,GObejctClass是所有对象的类结构的根。25. *在BoyClass中我们还定义了一个函数指针boy_born,也就是说这一函数指针也是所有Boy对象实例共有的,26. *所有的Boy实例都可以调用它;同样,如果需要的话,你也可以在类结构中定义其它数据成员。27. */28. struct_BoyClass29. 30. GObjectClassparent_class;31. void(*boy_born)(void);32. ;33. /*其余的函数定义包括三种:一种是取得Boy对象的类型ID的函数boy_get_type,这是必须有的;34. *另一种是创建Boy对象实例的函数boy_new和boy_new_with_*,这是非常清晰明了的创建对象的方式,当然你也可以用g_object_new函数来创建对象;35. *第三种是设定或取得Boy对象属性成员的值的函数boy_get_*和boy_set_*。36. *正常情况下这三种函数都是一个对象所必需的,另外一个函数boy_info用来显示此对象的当前状态*/37. GTypeboy_get_type(void);38. Boy*boy_new(void);39. gintboy_get_age(Boy*boy);40. voidboy_set_age(Boy*boy,intage);41. char*boy_get_name(Boy*boy);42. voidboy_set_name(Boy*boy,char*name);43. Boy*boy_new_with_name(gchar*name);44. Boy*boy_new_with_age(gintage);45. Boy*boy_new_with_name_and_age(gchar*name,gintage);46. voidboy_info(Boy*boy);47. #endif/_BOY_H_boy.c实现代码1. #includeboy.h2. /*用LAST_SIGNAL来表示最后一个信号(不用实现的信号)是一种非常良好的编程风格*/3. enumBOY_BORN,LAST_SIGNAL;4. staticgintboy_signalsLAST_SIGNAL=0;5. staticvoidboy_cry(void);6. staticvoidboy_born(void);7. staticvoidboy_init(Boy*boy);8. staticvoidboy_class_init(BoyClass*boyclass);9. 10. 11. GTypeboy_get_type(void)12. 13. staticGTypeboy_type=0;14. if(!boy_type)15. staticconstGTypeInfoboy_info=16. sizeof(BoyClass),17. NULL,NULL,18. (GClassInitFunc)boy_class_init,19. NULL,NULL,20. sizeof(Boy),21. 0,22. (GInstanceInitFunc)boy_init23. ;24. boy_type=g_type_register_static(G_TYPE_OBJECT,Boy,&boy_info,0);/*g_type_register_static函数用来注册对象的类型,25. * 第一个参数是表示此对象的父类的对象类型,我们这里是G_TYPE_OBJECT,这个宏用来表示 GObject的父类;26. *第二个参数表示此对象的名称,这里为Boy;27. *第三个参数是此对象的GTypeInfo结构型指针,这里赋值为&boyinfo;28. *第四个参数是对象注册成功后返回此对象的整型ID标识。*/29. 30. returnboy_type;31. 32. /*boy_init和boy_class_init,它们分别用来初始化实例结构和类结构。它们并不被在代码中明显调用,33. *关键是将其用宏转换为地址指针,然后赋值到GTypeInfo结构中,然后由GType系统自行处理,34. *同时将它们定义为静态的也是非常必要的。*/35. staticvoidboy_init(Boy*boy)36. 37. boy-age=0;38. boy-name=none;39. boy-cry=boy_cry;40. 41. staticvoidboy_class_init(BoyClass*boyclass)42. 43. boyclass-boy_born=boy_born;44. /*Boy对象定义了一个信号BOY_BORN,在对象创建时发出,表示Boy对象诞生。45. *同时还需要定义静态的整型指针数组来保存信号的标识46. */47. boy_signalsBOY_BORN=g_signal_new(boy_born,48. BOY_TYPE,49. G_SIGNAL_RUN_FIRST,50. G_STRUCT_OFFSET(BoyClass,boy_born),51. NULL,NULL,52. g_cclosure_marshal_VOID_VOID,53. G_TYPE_NONE,0,NULL);54. 在这段代码中,出现了实现Boy对象的关键函数,这是在Boy对象的定义中未出现的,也是没必要出现的。就是两个初始化函数,boy_init和boy_class_init,它们分别用来初始化实例结构和类结构。它们并不被在代码中明显调用,关键是将其用宏转换为地址指针,然后赋值到GTypeInfo结构中,然后由GType系统自行处理,同时将它们定义为静态的也是非常必要的。GTypeInfo结构中定义了对象的类型信息,包括以下内容:1. 包括类结构的长度(必需,即我们定义的BoyClass结构的长度);2. 基础初始化函数(base initialization function,可选);3. 基础结束化函数(base finalization function,可选);(以上两个函数可以对对象使用的内存来做分配和释放操作,使用时要用GBaseInitFunc和GBaseFinalizeFunc来转换为指针,本例中均未用到,故设为NULL。)4. 类初始化函数(即我们这里的boy_class_init函数,用GclassInit宏来转换,可选,仅用于类和实例类型);5. 类结束函数(可选);6. 实例初始化函数(可选,即我们这里的boy_init函数);7. 最后一个成员是GType变量表(可选)。定义好GTypeInfo结构后就可以用g_type_register_static函数来注册对象的类型了。g_type_register_static函数用来注册对象的类型第一个参数是表示此对象的父类的对象类型,我们这里是G_TYPE_OBJECT,这个宏用来表示GObject的父类;第二个参数表示此对象的名称,这里为Boy;第三个参数是此对象的GTypeInfo结构型指针,这里赋值为&boyinfo;第四个参数是对象注册成功后返回此对象的整型ID标识。g_object_new函数,用来创建一个基于G_OBJECT的对象,它可以有多个参数,第一个参数是上面说到的已注册的对象标识ID;第二个参数表示后面参数的数量,如果为0,则没有第三个参数;第三个参数开始类型都是GParameter类型,它也是一个结构型,定义为:struct GParameterconst gcha
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论