新闻  |   论坛  |   博客  |   在线研讨会
骁龙820A汽车 ADAS 分享方案介绍—GObject相关解析(一)
车载技术工程师 | 2018-06-26 15:32:54    阅读:547   发布文章

在以上的博客中,我们对多媒体处理框架进行阐述在接下来的博客中我们会对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具体的一些具体语法做出分析,以便在编写代码时使用。

*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客