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)对象的实例(多个,结构型,对象的具体实现)。
#ifndef _BOY_H_ #define _BOY_H_ #include <glib-object.h> /**BOY_TYPE宏封装了boy_get_type函数,可以直接取得并替代Boy对象的ID标识; * BOY(obj)宏是G_TYPE_CHECK_INSTANCE_CAST宏的再一次封装,目的是将一个Gobject对象强制转换为Boy对象 **/ #define BOY_TYPE (boy_get_type()) #define BOY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BOY_TYPE, Boy)) typedef struct _Boy Boy; typedef struct _BoyClass BoyClass; /**结构类型_Boy是Boy对象的实例,就是说我们每创建一个Boy对象,也就同时创建了一个Boy结构。 * Boy对象中的parent表示此对象的父类,GObject系统中所有对象的共同的根都是GObject类,所以这是必须的; * 其它的成员可以是公共的,这里包括表示年龄的age,表示名字的name和表示方法的函数指针cry, * 外部代码可以操作或引用它们。 **/ struct _Boy { GObject parent; gint age; gchar *name; void (*cry)(void); }; /**结构类型_BoyClass是Boy对象的类结构,它是所有Boy对象实例所共有的。 * BoyClass中的parent_class是GObjectClass,同GObject是所有对象的共有的根一样,GObejctClass是所有对象的类结构的根。 * 在BoyClass中我们还定义了一个函数指针boy_born,也就是说这一函数指针也是所有Boy对象实例共有的, * 所有的Boy实例都可以调用它;同样,如果需要的话,你也可以在类结构中定义其它数据成员。 **/ struct _BoyClass { GObjectClass parent_class; void (*boy_born)(void); }; /**其余的函数定义包括三种:一种是取得Boy对象的类型ID的函数boy_get_type,这是必须有的; * 另一种是创建Boy对象实例的函数boy_new和boy_new_with_*,这是非常清晰明了的创建对象的方式,当然你也可以用g_object_new函数来创建对象; * 第三种是设定或取得Boy对象属性成员的值的函数boy_get_*和boy_set_*。 * 正常情况下这三种函数都是一个对象所必需的,另外一个函数boy_info用来显示此对象的当前状态**/ GType boy_get_type(void); Boy *boy_new(void); gint boy_get_age(Boy *boy); void boy_set_age(Boy *boy, int age); char *boy_get_name(Boy *boy); void boy_set_name(Boy *boy, char *name); Boy *boy_new_with_name(gchar *name); Boy * boy_new_with_age(gint age); Boy * boy_new_with_name_and_age(gchar *name, gint age); void boy_info(Boy *boy); #endif // _BOY_H_(2)实现文件boy.c:
#include "boy.h" /**用LAST_SIGNAL来表示最后一个信号(不用实现的信号)是一种非常良好的编程风格**/ enum { BOY_BORN, LAST_SIGNAL }; static gint boy_signals[LAST_SIGNAL] = { 0 }; static void boy_cry (void); static void boy_born(void); static void boy_init(Boy *boy); static void boy_class_init(BoyClass *boyclass); /**g_type_register_static函数用来注册对象的类型,它的第一个参数是表示此对象的父类的对象类型,我们这里是G_TYPE_OBJECT,这个宏用来表示GObject的父类; * 第二个参数表示此对象的名称,这里为"Boy"; * 第三个参数是此对象的GTypeInfo结构型指针,这里赋值为&boyinfo; * 第四个参数是对象注册成功后返回此对象的整型ID标识。 **/ GType boy_get_type(void) { static GType boy_type = 0; if(!boy_type) { static const GTypeInfo boy_info = { sizeof(BoyClass), NULL,NULL, (GClassInitFunc)boy_class_init, NULL,NULL, sizeof(Boy), 0, (GInstanceInitFunc)boy_init }; boy_type = g_type_register_static(G_TYPE_OBJECT,"Boy",&boy_info,0); } return boy_type; } /**boy_init和boy_class_init,它们分别用来初始化实例结构和类结构。它们并不被在代码中明显调用, * 关键是将其用宏转换为地址指针,然后赋值到GTypeInfo结构中,然后由GType系统自行处理, * 同时将它们定义为静态的也是非常必要的。 **/ static void boy_init(Boy *boy) { boy->age = 0; boy->name = "none"; boy->cry = boy_cry; } static void boy_class_init(BoyClass *boyclass) { boyclass->boy_born = boy_born; /**Boy对象定义了一个信号BOY_BORN,在对象创建时发出,表示Boy对象诞生。 *同时还需要定义静态的整型指针数组来保存信号的标识 **/ boy_signals[BOY_BORN] = g_signal_new("boy_born", BOY_TYPE, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(BoyClass,boy_born), NULL,NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL); } Boy *boy_new(void) { Boy *boy; boy = g_object_new(BOY_TYPE, NULL); //g_signal_emit向指定义对象的实例发射信号 g_signal_emit(boy, boy_signals[BOY_BORN], 0); return boy; } int boy_get_age(Boy *boy) { return boy->age; } void boy_set_age(Boy *boy, int age) { boy->age = age; } char *boy_get_name(Boy *boy) { return boy->name; } void boy_set_name(Boy *boy, char *name) { boy->name = name; } Boy* boy_new_with_name(gchar *name) { Boy *boy; boy = boy_new(); boy_set_name(boy, name); return boy; } Boy* boy_new_with_age(gint age) { Boy* boy; boy = boy_new(); boy_set_age(boy, age); return boy; } Boy *boy_new_with_name_and_age(gchar *name, gint age) { Boy *boy; boy = boy_new(); boy_set_name(boy,name); boy_set_age(boy,age); return boy; } static void boy_cry (void) { g_print("---The Boy is crying......\n"); } static void boy_born(void) { g_print("---Message : A boy was born.\n"); } void boy_info(Boy *boy) { g_print("---The Boy name is %s\n", boy->name); g_print("---The Boy age is %d\n", boy->age); }
#ifndef _MAN_H_ #define _MAN_H_ /**********Man继承自Boy对象,Man对象在Boy对象的基础上又增加了一个属性job和一个方法bye***********/ #include "boy.h" #define MAN_TYPE (man_get_type()) #define MAN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), MAN_TYPE, Man)) typedef struct _Man Man; typedef struct _ManClass ManClass; struct _Man { Boy parent; gchar *job; void (*bye)(void); }; struct _ManClass { BoyClass parent_class; }; GType man_get_type(); Man *man_new(void); gchar *man_get_job(Man *man); void man_set_job(Man *man, gchar *job); Man *man_new_with_name_age_and_job(gchar *name, gint age, gchar *job); void man_info(Man *man); #endif // _MAN_H_(2)实现文件man.c:
#include "man.h" static void man_bye(void); static void man_init(Man *man); static void man_class_init(Man *man); /**关键在于定义对象时将父对象实例定义为Boy,父类设定为BoyClass,在注册此对象时将其父对象类型设为BOY_TYPE。 **/ GType man_get_type(void) { static GType man_type = 0; if(!man_type) { static const GTypeInfo man_info = { sizeof(ManClass), NULL, NULL, (GClassInitFunc)man_class_init, NULL, NULL, sizeof(Man), 0, (GInstanceInitFunc)man_init }; /**在注册此对象时将其父对象类型设为BOY_TYPE**/ man_type = g_type_register_static(BOY_TYPE, "Man", &man_info, 0); } return man_type; } static void man_init(Man *man) { man->job = "none"; man->bye = man_bye; } static void man_class_init(Man *man) { } Man* man_new(void) { Man *man; man = g_object_new(MAN_TYPE, 0); return man; } gchar* man_get_job(Man *man) { return man->job; } void man_set_job(Man *man, gchar *job) { man->job = job; } Man* man_new_with_name_age_and_job(gchar *name, gint age, gchar *job) { Man *man; man = man_new(); boy_set_name(BOY(man), name); boy_set_age(BOY(man), age); man_set_job(man, job); return man; } static void man_bye(void) { g_print("+++Goodbye everyone!\n"); } /**在设定对象属性时如用到父对象的属性要强制转换下,如取得对象的name属性,就必须用BOY(obj)->name, *因为Man本身没有name属性,而其父对象Boy有,所以用BOY宏将其强制为Boy类型的对象。 **/ void man_info(Man *man) { /**定义对象时将父对象实例定义为Boy,父类设定为BoyClass**/ g_print("+++the man name is %s\n", BOY(man)->name); g_print("+++the man age is %d\n", BOY(man)->age); g_print("+++the man job is %s\n", man->job); }
#include <glib.h> #include "boy.h" #include "man.h" int main(int argc, char *argv[]) { Boy *tom, *peter; Man *green, *brown; g_type_init();//注意,初始化类型系统,必需 g_print("**********************\n"); tom = boy_new_with_name("Tom"); tom->cry(); boy_info(tom); g_print("**********************\n"); peter = boy_new_with_name_and_age("Peter", 10); peter->cry(); boy_info(peter); g_print("**********************\n"); g_print("######################\n"); green = man_new(); /**设定Man对象的name属性用到其父对象Boy的方法**/ boy_set_name(BOY(green), "Green"); boy_set_age(BOY(green), 28); man_set_job(green, "Doctor"); man_info(green); green->bye(); g_print("######################\n"); brown = man_new_with_name_age_and_job("Brown", 30, "Teacher"); man_info(brown); brown->bye(); g_print("######################\n"); return TRUE; }makefile文件:
CC = gcc all: $(CC) -c boy.c `pkg-config --cflags glib-2.0 gobject-2.0` $(CC) -c man.c `pkg-config --cflags glib-2.0 gobject-2.0` $(CC) -c main.c `pkg-config --cflags glib-2.0 gobject-2.0` $(CC) -o simple boy.o man.o main.o `pkg-config --libs glib-2.0 gobject-2.0`运行结果:
#define G_DEFINE_TYPE(TN, t_n, T_P) G_DEFINE_TYPE_EXTENDED (TN, t_n, T_P, 0, {})
#define G_DEFINE_TYPE_EXTENDED(TN, t_n, T_P, _f_, _C_) _G_DEFINE_TYPE_EXTENDED_BEGIN (TN, t_n, T_P, _f_) {_C_;} _G_DEFINE_TYPE_EXTENDED_END()
#define _G_DEFINE_TYPE_EXTENDED_BEGIN(TypeName, type_name, TYPE_PARENT, flags) \ \ static void type_name##_init (TypeName *self); \ static void type_name##_class_init (TypeName##Class *klass); \ static gpointer type_name##_parent_class = NULL; \ static void type_name##_class_intern_init (gpointer klass) \ { \ type_name##_parent_class = g_type_class_peek_parent (klass); \ type_name##_class_init ((TypeName##Class*) klass); \ } \ \ GType \ type_name##_get_type (void) \ { \ static volatile gsize g_define_type_id__volatile = 0; \ if (g_once_init_enter (&g_define_type_id__volatile)) \ { \ GType g_define_type_id = \ g_type_register_static_simple (TYPE_PARENT, \ g_intern_static_string (#TypeName), \ sizeof (TypeName##Class), \ (GClassInitFunc) type_name##_class_intern_init, \ sizeof (TypeName), \ (GInstanceInitFunc) type_name##_init, \ (GTypeFlags) flags); \ { /* custom code follows */ #define _G_DEFINE_TYPE_EXTENDED_END() \ /* following custom code */ \ } \ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); \ } \ return g_define_type_id__volatile; \ } /* closes type_name##_get_type() */在上述代码中可以使用G_DEFINE_TYPE(Boy, boy, G_TYPE_OBJECT);代替:
static void boy_init(Boy *boy); static void boy_class_init(BoyClass *boyclass); GType boy_get_type(void) { static GType boy_type = 0; if(!boy_type) { static const GTypeInfo boy_info = { sizeof(BoyClass), NULL,NULL, (GClassInitFunc)boy_class_init, NULL,NULL, sizeof(Boy), 0, (GInstanceInitFunc)boy_init }; boy_type = g_type_register_static(G_TYPE_OBJECT,"Boy",&boy_info,0); } return boy_type; }
#ifndef _BOY_H_ #define _BOY_H_ #include <glib-object.h> /**BOY_TYPE宏封装了boy_get_type函数,可以直接取得并替代Boy对象的ID标识; * BOY(obj)宏是G_TYPE_CHECK_INSTANCE_CAST宏的再一次封装,目的是将一个Gobject对象强制转换为Boy对象 **/ typedef struct _Boy Boy; typedef struct _BoyClass BoyClass; typedef struct _BoyPrivate BoyPrivate; #define BOY_TYPE (boy_get_type()) #define BOY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BOY_TYPE, Boy)) #define BOY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), BOY_TYPE, BoyPrivate)) /**结构类型_Boy是Boy对象的实例,就是说我们每创建一个Boy对象,也就同时创建了一个Boy结构。 * Boy对象中的parent表示此对象的父类,GObject系统中所有对象的共同的根都是GObject类,所以这是必须的; * 其它的成员可以是公共的,这里包括表示年龄的age,表示名字的name和表示方法的函数指针cry, * 外部代码可以操作或引用它们。 **/ struct _Boy { GObject parent; BoyPrivate *priv; gint age; gchar *name; void (*cry)(void); }; /**结构类型_BoyClass是Boy对象的类结构,它是所有Boy对象实例所共有的。 * BoyClass中的parent_class是GObjectClass,同GObject是所有对象的共有的根一样,GObejctClass是所有对象的类结构的根。 * 在BoyClass中我们还定义了一个函数指针boy_born,也就是说这一函数指针也是所有Boy对象实例共有的, * 所有的Boy实例都可以调用它;同样,如果需要的话,你也可以在类结构中定义其它数据成员。 **/ struct _BoyClass { GObjectClass parent_class; void (*boy_born)(void); }; struct _BoyPrivate { gint weight; }; /**其余的函数定义包括三种:一种是取得Boy对象的类型ID的函数boy_get_type,这是必须有的; * 另一种是创建Boy对象实例的函数boy_new和boy_new_with_*,这是非常清晰明了的创建对象的方式,当然你也可以用g_object_new函数来创建对象; * 第三种是设定或取得Boy对象属性成员的值的函数boy_get_*和boy_set_*。 * 正常情况下这三种函数都是一个对象所必需的,另外一个函数boy_info用来显示此对象的当前状态**/ GType boy_get_type(void); Boy *boy_new(void); gint boy_get_age(Boy *boy); void boy_set_age(Boy *boy, int age); char *boy_get_name(Boy *boy); void boy_set_name(Boy *boy, char *name); Boy *boy_new_with_name(gchar *name); Boy * boy_new_with_age(gint age); Boy * boy_new_with_name_and_age(gchar *name, gint age); void boy_info(Boy *boy); #endif // _BOY_H_
#include "boy.h" /**用LAST_SIGNAL来表示最后一个信号(不用实现的信号)是一种非常良好的编程风格**/ enum { BOY_BORN, LAST_SIGNAL }; static gint boy_signals[LAST_SIGNAL] = { 0 }; static void boy_cry (void); static void boy_born(void); //static void boy_init(Boy *boy); //static void boy_class_init(BoyClass *boyclass); G_DEFINE_TYPE(Boy, boy, G_TYPE_OBJECT); /**g_type_register_static函数用来注册对象的类型,它的第一个参数是表示此对象的父类的对象类型,我们这里是G_TYPE_OBJECT,这个宏用来表示GObject的父类; * 第二个参数表示此对象的名称,这里为"Boy"; * 第三个参数是此对象的GTypeInfo结构型指针,这里赋值为&boyinfo; * 第四个参数是对象注册成功后返回此对象的整型ID标识。 **/ //static void boy_init(Boy *boy); //static void boy_class_init(BoyClass *boyclass); //GType boy_get_type(void) //{ // static GType boy_type = 0; // if(!boy_type) { // static const GTypeInfo boy_info = { // sizeof(BoyClass), // NULL,NULL, // (GClassInitFunc)boy_class_init, // NULL,NULL, // sizeof(Boy), // 0, // (GInstanceInitFunc)boy_init // }; // boy_type = g_type_register_static(G_TYPE_OBJECT,"Boy",&boy_info,0); // } // return boy_type; //} /**boy_init和boy_class_init,它们分别用来初始化实例结构和类结构。它们并不被在代码中明显调用, * 关键是将其用宏转换为地址指针,然后赋值到GTypeInfo结构中,然后由GType系统自行处理, * 同时将它们定义为静态的也是非常必要的。 **/ static void boy_init(Boy *boy) { boy->age = 0; boy->name = "none"; boy->cry = boy_cry; BoyPrivate *d; d = boy->priv = BOY_GET_PRIVATE(boy); memset(d, 0, sizeof(*d)); d->weight = 100; } static void boy_class_init(BoyClass *boyclass) { boyclass->boy_born = boy_born; /**Boy对象定义了一个信号BOY_BORN,在对象创建时发出,表示Boy对象诞生。 *同时还需要定义静态的整型指针数组来保存信号的标识 **/ boy_signals[BOY_BORN] = g_signal_new("boy_born", BOY_TYPE, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(BoyClass,boy_born), NULL,NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL); g_type_class_add_private(boyclass, sizeof(BoyPrivate)); } Boy *boy_new(void) { Boy *boy; boy = g_object_new(BOY_TYPE, NULL); //g_signal_emit向指定义对象的实例发射信号 g_signal_emit(boy, boy_signals[BOY_BORN], 0); return boy; } int boy_get_age(Boy *boy) { return boy->age; } void boy_set_age(Boy *boy, int age) { boy->age = age; } char *boy_get_name(Boy *boy) { return boy->name; } void boy_set_name(Boy *boy, char *name) { boy->name = name; } Boy* boy_new_with_name(gchar *name) { Boy *boy; boy = boy_new(); boy_set_name(boy, name); return boy; } Boy* boy_new_with_age(gint age) { Boy* boy; boy = boy_new(); boy_set_age(boy, age); return boy; } Boy *boy_new_with_name_and_age(gchar *name, gint age) { Boy *boy; boy = boy_new(); boy_set_name(boy,name); boy_set_age(boy,age); return boy; } static void boy_cry (void) { g_print("---The Boy is crying......\n"); } static void boy_born(void) { g_print("---Message : A boy was born.\n"); } void boy_info(Boy *boy) { g_print("---The Boy name is %s\n", boy->name); g_print("---The Boy age is %d\n", boy->age); g_print("---The Boy weight is %d\n", boy->priv->weight); }将struct _BoyPrivate加入struct _Boy中,先将私有成员加到类中g_type_class_add_private(boyclass, sizeof(BoyPrivate));然后使用boy->priv = BOY_GET_PRIVATE(boy);返回指向私有结构体的指针(第一次调用时会分配内存空间,创建对象后直接返回指针)。