"); //-->
在以上的博客中,我们对多媒体处理框架进行阐述在接下来的博客中我们会对GObject来进行分析,用以实现项目需求。
• 简单的来说, GObject是一个程序库,它可以帮助我们使用C语言
编写面向对象的程序。
• 很多人被灌输了这样一种概念:要写面向对象程序,那么就需要
学习一种面向对象编程语言,例如C++、 Java、 C# 等等,而 C 语
言是用来编写结构化程序的。事实上,面向对象只是一种编程思
想,不是一种编程语言。换句话说,面向对象是一种游戏规则,
它不是游戏。 GObject 告诉我们,使用 C 语言编写程序时,可以
运用面向对象这种编程思想。
GObject中模拟类的数据封装
• C++是典型的使用面向对象编程思想的语言,我们在这里将GObject与C++最
对比,借助C++来理解GObject的基本编程框架。
• 先观察以下C++ 代码:
#include <iostream> class MyObject { public: MyObject() {std::cout << "对象初始化" << std::endl;} }; int main() { MyObject my_obj; return 0; }
只要具备一点C++类的知识,上述C++代码应该不难理解。下面用GObject对其进行逐步模拟。
在 GObject 世界里,类是两个结构体的组合,一个是实例结构体,另一个是类结构体。例如, MyObject 是实例结构体,MyObjectClass 是类结构体,它们合起来便可以称为 MyObject类。
[类结构体] 以下 C++ 代码: class MyObject; 用 GObject 可表述为: #include <glib-object.h> typedef struct _MyObjectClass { GObjectClass parent_class; } MyObjectClass;
[实例结构体] 以下 C++ 代码: class MyObject { }; 用 GObject 可表述为: #include <glib-object.h> typedef struct _MyObject{ GObject parent_instance; } MyObject; typedef struct _MyObjectClass { GObjectClass parent_class; } MyObjectClass; G_DEFINE_TYPE(MyObject, my_object, G_TYPE_OBJECT);
• 在GObject中一个对象的产生遵循如下原则:
• 如果产生的是该类的第一个实例,那么先分配Class结构体,再分配针对该实例的结构体。否则直接分配针对该实例的结构。也就是说在Class结构体中所有的内容,是通过该类生成的实例所公有的。而实例化每个对象时,为其单独分配专门的实例用结构体。
• 也许你会注意到,MyObject类的实例结构体的第一个成员是 GObject 结构体,MyObject类的类结构体的第一个成员是 GObjectClass 结构体。其实,GObject结构体与 GObjectClass 结构体分别是 GObject类的实例结构体与类结构体,当它们分别作为 MyObject类的实例结构体与类结构体的第一个成员时,这意味着 MyObject类继承自 GObject类。
• 每个类必须定义为两个结构体:它的类结构体和它的实例结构体。所有的类结构体的第一个成员必须是一个GTypeClass结构,所有的实例结构体的第一个成员必须是GTypeInstance结构。
使用GObject 类作为父类的原因
• 为什么 MyObject类一定要将 GObject 类作为父类?主要是因为
GObject 类具有以下功能:
• 基于引用计数的内存管理
• 对象的构造函数与析构函数
• 可设置对象属性的 set/get 函数
• 易于使用的信号机制
• 在后面再具体讨论这些优点
G_DEFINE_TYPE
• 既然已经有了类的类结构体和实例结构体了,怎么让GObject系统知道我们
这个类呢?就需要想GObject系统中注册这个类,GObject中向我们ᨀ供了一
个简单的宏来完成这个任务:G_DEFINE_TYPE。
G_DEFINE_TYPE (MyObject, my_object, G_TYPE_OBJECT);
• G_DEFINE_TYPE 可以让 GObject 库的数据类型系统能够识别我们所定义的MyObject类类型,它接受三个参数,第一个参数是类名,即 MyObject;第
二个参数则是类的成员函数(面向对象术语称之为”方法“或”行为“)名
称的前缀,例如 my_object_get_type 函数即为 MyObject类的一个成员函数,"my_object" 是它的前缀;第三个参数则指明MyObject类类型的父类型为 G_TYPE_OBJECT。嗯,这个 G_TYPE_OBJECT也是一个宏。
G_DEFINE_TYPE宏是怎么完成向GObject系统中注册这个类呢?
#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); \ } \ / gulong\ type_name##_get_type (void) \ { \ static volatile gsize g_define_type_id__volatile = 0; \ if (g_once_init_enter (&g_define_type_id__volatile)) \ { \ gulongg_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() */
下面还是以MyObject为例,看下面的代码: G_DEFINE_TYPE(MyObject, my_object, G_TYPE_OBJECT);
那么便可将 G_DEFINE_TYPE 展开为下面的 C 代码: static void my_object_init(MyObject * self); static void my_object_class_init(MyObjectClass * klass); static gpointer my_object_parent_class = ((void *) 0); static void my_object_class_intern_init(gpointer klass) { my_object_parent_class = g_type_class_peek_parent(klass); my_object_class_init((MyObjectClass *) klass); } GType my_object_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(((GType) ((20) << (2))), g_intern_static_string("MyObject"), sizeof(MyObjectClass), (GClassInitFunc) my_object_class_intern_init, sizeof(MyObject), (GInstanceInitFunc) my_object_init, (GTypeFlags) 0); } return g_define_type_id__volatile; };
GObject 类型系统之所以能够接受 MyObject 这个[类]的类型,完全拜 my_object_get_type 函数所赐。因为my_object_get_type函
数调用了g_type_register_static_simple函数,后者由 GObject类型系统ᨀ供,其主要职责就是为 GObject 类型系统扩充人马。
my_object_get_type向 g_type_register_static_simple汇报:我手里有个MyObject 类,它由GObject类派生,它的姓名、三围、籍
贯、民族分别为 ……@#$%^&*(……balaba……balaba……),然后 g_type_register_static_simple 就为MyObject登记造册,从此
GObject类型系统中就有了MyObject这号人物了。
my_object_get_type 函数的定义利用了 static 变量实现了以下功能:
my_object_get_type 第一次被调用时,会向 GObject 类型系统注册 MyObject 类型,然后对 MyOject 类进行实例化,产生对象,
最后返回对象的数据类型的 ID;
从 my_object_get_type 第二次被调用开始,它就只进行 MyOject 类的实例化,不会再重复向 GObject 类型系统注册类型。
那么 my_object_get_type 会被谁调用?它会被 g_object_new 调用,所有 GObject 类派生的类型,皆可由 g_object_new 函数进
行实例化,例如:
MyObject *my_obj = g_object_new(my_object_get_type(), NULL);
所以 G_DEFINE_TYPE 宏就出现了,它悄悄的执行着这些琐事……所以,C++们就出现了,class们悄悄的执行着这些琐事……
那么,G_DEFINE_TYPE 宏是怎么执行完这些琐事的呢?从上面的代码中可以看出来,如果在.c文件中声明一个G_DEFINE_TYPE宏的话,
就会自动生成一个my_object_get_type (void)函数, 而这也就是需要在.h头文件中声明的原因。从上面的代码我们还可以看出,
G_DEFINE_TYPE 声明了两个函数, 但是并没有实现, 需要定义对象的用户自己去实现, 这两个函数是:
static void my_object_init(MyObject * self);
static void my_object_class_init(MyObjectClass * klass);
这两个函数是对象的初始化函数, 相当于C++中的构造函数, 第一个函数在每个对象创建的时候都会被调用, 第二个函数只有在第一
次创建对象的时候才会被调用。
总的来说
使用 GObject 库模拟基于类的数据封装,或者用专业术语来说,即 GObject 类类型的子类化,念起来比较拗口,便干脆简称 GObject 子类化,其过程只需要以下五步:
1)在 .h 文件中包含 glib-object.h;
2)在 .h 文件中构建实例结构体与类结构体,并分别将 GObject 类的实例结构体与类结构体置于成员之首;
3)在 .h 文件中定义 PREFIX_OBJECT_TYPE宏,并声明 prefix_object_get_type函数;
4)在 .h 文件中继续完成上面那5个宏定义;
5)在 .c 文件中调用 G_DEFINE_TYPE 宏产生类型注册代码。
我们对GObject特性的简介就到这里,在后续的博客中,我们将会对GObject具体的一些具体语法做出分析,以便在编写代码时使用。
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。