本文主要是介绍Gobject tutorial 二,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Type system and registration process
Gobject本身是一个基础对象类型。我们平常并不会直接使用。通常我们都是使用其衍生类型,比如,各种类型的GtkWidget。
本次,我们聊聊如何创建一个Gobject的衍生对象。我们的例子是实现一个表示数字的对象。尽管c语言中有int、double等类型来表示数字。然而我们的例子与他们并不冲突。对此,我们需要从更抽象的层次来理解。如果你去了解抽象代数,或许你会对此有更深刻的认识。
Name convention
首先,我们来了解一下命名规则。对象类型的名称包含名字空间和对象名字两部分。比如说,Gobject包含名字空间G和对象名字Object,GtkWidget包含名字空间Gtk和对象名字Widget。在我们的例子中,我们使用T作为名字空间,Double作为对象名字。TDouble就是我们构建的新的类型的名字。它是由GObject衍生而来,它代表一个真实的数字,一个真实的阿拉伯数字,数字的类型就是c语言中的double。
Define TDoubleClass and TDouble
我们的新类型TDoube,包含TDouble类和TDouble实例。因此,对于一个完整的TDouble类型,我们要分两部分进行定义。
对于TDouble的类,以C语言的形式进行表示时,它的名字是TDoubleClass,定义如下:
typedef struct _TDoubleClass TDoubleClass;
struct _TDoubleClass {GObjectClass parent_class;
};
我们要说明的是,表示TDoubleClass的结构体的第一个成员必须是其父类结构体。那么,问题来了,为啥这么做呢?如果去研究GTK源码,你会发现其中有很多强制类型转化来简化获取结构体成员的操作。此处这么定义就能说明为什么可以做强制类型转换。
对于TDouble的实例,以C语言的形式进行表示时,它的名字是TDouble,定义如下:
typedef struct _TDouble TDouble;
struct _TDouble {GObject parent;double value;
};
同理, 表示TDouble的结构体的第一个成员必须是其父实例结构体。原因同上。
Creation process of a child of GObject
新类型TDouble的创建包含以下几个步骤:
- 将TDouble类型注册到类型系统中。
- 类型系统为TDoubleClass 和TDouble分配内存空间。
- 初始化TDoubleClass。
- 初始化TDouble。
Registration
对于注册本身来说,分为静态注册和动态注册两种。两者的区别在于,即使特定类型的所有实例都被销毁了,静态注册的类型也不会销毁其类。而动态注册的类型在其最后一个实例销毁时,其类也会被销毁。值得注意的是,GObject是静态注册的,同时,它的衍生对象类型静态注册的。静态注册的函数如下:
GType
g_type_register_static (GType parent_type,const gchar *type_name,const GTypeInfo *info,GTypeFlags flags);
GTypeInfo结构体,正如其名,它存放的是关于对象类型的信息。其定义如下:
typedef struct _GTypeInfo GTypeInfo;struct _GTypeInfo
{/* interface types, classed types, instantiated types */
//用于表示类结构体的大小,对于Tdouble,此值就是siezof(TDoubleClass)guint16 class_size;
//下边两个成员用于initialize/finalize类的动态成员GBaseInitFunc base_init;GBaseFinalizeFunc base_finalize;/* interface types, classed types, instantiated types */GClassInitFunc class_init; //初始化类的静态成员。GClassFinalizeFunc class_finalize; //finalize classgconstpointer class_data; //用户提供的参数,供上述两函数使用/* instantiated types */guint16 instance_size; //实例的大小,对于TDouble,此值就是sizeoof(TDouble)guint16 n_preallocs;//ignoredGInstanceInitFunc instance_init; //初始化实例成员/* value handling */const GTypeValueTable *value_table; //对基类有用,对于GObject的衍生类,无用。
};
下面举例说明,一个完成的注册新类型的流程。
1 #include <glib-object.h>2 3 #define T_TYPE_DOUBLE (t_double_get_type ())4 5 typedef struct _TDouble TDouble;6 struct _TDouble {7 GObject parent;8 double value;9 };
10
11 typedef struct _TDoubleClass TDoubleClass;
12 struct _TDoubleClass {
13 GObjectClass parent_class;
14 };
15
16 static void
17 t_double_class_init (TDoubleClass *class) {
18 }
19
20 static void
21 t_double_init (TDouble *self) {
22 }
23 /*此函数返回TDouble对象的类型,对于此功能的函数命名,有个规则,那就是<name space>_<name>_get_type ,为了更方便使用其返回值,我们定义了一个宏来指代其返回值。
这个宏也是有命名规则的。即<NAME_SPACE>_TYPE_<NAME> */
24 GType
25 t_double_get_type (void) {
26 static GType type = 0;
27 GTypeInfo info;
28
29 if (type == 0) {
30 info.class_size = sizeof (TDoubleClass);
31 info.base_init = NULL;
32 info.base_finalize = NULL;
33 info.class_init = (GClassInitFunc) t_double_class_init;
34 info.class_finalize = NULL;
35 info.class_data = NULL;
36 info.instance_size = sizeof (TDouble);
37 info.n_preallocs = 0;
38 info.instance_init = (GInstanceInitFunc) t_double_init;
39 info.value_table = NULL;
40 type = g_type_register_static (G_TYPE_OBJECT, "TDouble", &info, 0);
41 }
42 return type;
43 }
44
45 int
46 main (int argc, char **argv) {
47 GType dtype;
48 TDouble *d;
49
50 dtype = t_double_get_type (); /* or dtype = T_TYPE_DOUBLE */
51 if (dtype)
52 g_print ("Registration was a success. The type is %lx.\n", dtype);
53 else
54 g_print ("Registration failed.\n");
55
56 d = g_object_new (T_TYPE_DOUBLE, NULL);
57 if (d)
58 g_print ("Instantiation was a success. The instance address is %p.\n", d);
59 else
60 g_print ("Instantiation failed.\n");
61 g_object_unref (d); /* Releases the object d. */
62
63 return 0;
64 }
65
G_DEFINE_TYPE macro
上述的注册套路是固定的。因此,可以定义宏来简化编程。这个宏就是G_DEFINE_TYPE。
G_DEFINE_TYPE宏的主要工作如下:
- 声明类初始化函数,函数命名规则为
<name space>_<name>_class_init。对于TDouble来说,这个函数就叫做t_double_class_init。此处只负责声明,实现部分由用户负责。
- 声明实例初始化函数,函数命名规则为
<name space>_<name>_init。对于TDouble来说,这个函数名就是t_double_init。同样的,此处只负责声明,实现部分由用户负责。
- 定义一个静态变量,此变量指向其父类。这个变量的命名规则为
<name space>_<name>_parent_class。对于TDouble来说,这个变量名就是t_double_parent_class。
- 定义了一个
<name space>_<name>_get_type函数。对于TDouble来说,就是定义了t_doule_get_type.对于使用此宏的对象类型来说,已经完成对对象类型的注册。
使用此宏,上述例子可以写成这样。
#include <glib-object.h>#define T_TYPE_DOUBLE (t_double_get_type ())typedef struct _TDouble TDouble;
struct _TDouble {GObject parent;double value;
};typedef struct _TDoubleClass TDoubleClass;
struct _TDoubleClass {GObjectClass parent_class;
};G_DEFINE_TYPE (TDouble, t_double, G_TYPE_OBJECT)static void
t_double_class_init (TDoubleClass *class) {
}static void
t_double_init (TDouble *self) {
}int
main (int argc, char **argv) {GType dtype;TDouble *d;dtype = t_double_get_type (); /* or dtype = T_TYPE_DOUBLE */if (dtype)g_print ("Registration was a success. The type is %lx.\n", dtype);elseg_print ("Registration failed.\n");d = g_object_new (T_TYPE_DOUBLE, NULL);if (d)g_print ("Instantiation was a success. The instance address is %p.\n", d);elseg_print ("Instantiation failed.\n");g_object_unref (d);return 0;
}
这篇关于Gobject tutorial 二的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!