Gobject C语言库 I 对象与它的类定义
Gobject 时一个为C语言提供面向对象编程的基础库。 作为一门基础语言,C语言在操作系统层面有着绝对统治的应用,但是它是一门过程语言,在面对业务处理上,由于缺乏足够的抽象,编写起来非常费力。为了减少这些缺陷,C语言生态圈也出现了不同的基础库,帮助想使用面向对象进行C语言开发的工程师进行开发,Gobject就是其中之一。
在面向对象的语言里,每一个事物或者业务会被抽象为一种类型,每一个具体的事物都是这个类的实例。在声明和创建一个方法通常用下面的格式:
VcamSource source=new VcamSouce(......);
在Gobject中,通常会这样写:
VcamSource *source;
source=g_object_new(VCAM_TYPE_SOURCE, NULL);
VCAM_TYPE_SOURCE 是一个宏定义,用来返回系统中注册的VcamSource类型。
在面向对象的语言里因为有复杂的对象与类继承管理,所以通常只需要声明VcamSource 类就可以了:
Class VcamSource{
public:
....
private:
.....
}
在C语言中是缺乏这些的,所以Gobject提供类自己的继承链和类型注册系统,在Gobject中,我们通常需要声明一个抽象类型和它的类型描述类:
#ifndef VCAM_SOURCE_H
#define VCAM_SOURCE_H
#include <glib-object.h>
#define VCAM_TYPE_SOURCE (vcam_source_get_type ())
//抽象类型
typedef struct _VcamSoure VcamSource;
struct _VcamSoure {
GObject parent_instance;
};
//抽象类型的类
typedef struct _VcamSoureClass VcamSourceClass;
struct _VcamSoureClass {
GObjectClass parent_class;
};
GType vcam_source_get_type(void);
#endif
可以看出VcamSource抽象类型其实就是一个struct 结构体, 它包含至少一个属性GObject parent_instance;, 这表示的是一个继承关系: VcamSource继承自 GObject (这个属性必须为第一个元素)。
_VcamSoureClass 是VcamSource的类型,它用来描述VcamSource和包含这一个类事务的通用属性或方法。它的第一个属性是GObjectClass,也表示的是继承关系,且 GObjectClass正是GObject的类。
在这个定义中,宏定义VCAM_TYPE_SOURCE对应的是一个返回GType 类型的方法,他正对应Gobject创建实例的第一个参数:
source=g_object_new(VCAM_TYPE_SOURCE, NULL);
VCAM_TYPE_SOURCE实际上是vcam_source_get_type()方法的调用; 这个方法主要做两件事情:声明一个VcamSource的Gtype类型;把这个类型发注册到Gobject的类型系统里,这样才可以通过g_object_new来初始化。它的具体实现是在VcamSource.c文件里;vcam_source_get_type的实现内容大概如下:
GType vcam_source_get_type(void)
{
static GType source_type = 0;
if (!media_type)
{
//声明一个GTypeInfo类型
static const GTypeInfo source_info = {
sizeof(VcamSourceClass), /* class structure size */
NULL, /* base class initializer */
NULL, /* base class finalizer */
(GClassInitFunc)VcamSource_class_init, /* class initializer */
NULL, /* class finalizer */
NULL, /* class data */
sizeof(VcamSource), /* instance structure size */
16, /* preallocated instances */
NULL, /* instance initializer */
NULL /* function table */
};
//将GTypeInfo info注册到Gobject的type系统里
media_type = g_type_register_static(
G_TYPE_OBJECT, /* parent class */
"Media", /* type name */
&media_info, /* GTypeInfo struct (above) */
0); /* flags */
}
return source_type ;
}
g_type_register_static(parent_id, name, type_info, options) 用于类型的注册,他的参数如下:
parent_id: 父类标识,Object类的标识就是通过G_TYPE_OBJECT宏获取
name: 类型名称
typeinfo: GTypeInfo结构,里面包含了对新类型的描述
options: 标识符,例如标准虚类等。
在看一下,source_info的声明,里面包含了很多内容,我们需要了解的包含基础类的初始化,class类型的初始化,实例的初始化等等。这些方法的声明如下:
typedef void (GBaseInitFunc) (gpointer g_class);
typedef void (GBaseFinalizeFunc) (gpointer g_class);
typedef void (GClassInitFunc) (gpointer g_class, gpointer class_data);
typedef void (GClassFinalizeFunc) (gpointer g_class, gpointer class_data);
typedef void (*GInstanceInitFunc) (GTypeInstance *instance, gpointer g_class);
如果要一个一个实现,确实也是很费劲的事情,GObject也考虑了这一点,所以提供了一个新的宏定义,它是声明在.c文件里:
#include "VcamSource.h"
G_DEFINE_TYPE(VcamSource, vcam_source, G_TYPE_OBJECT)
static void
vcam_source_class_init(VcamSourceClass* class) { //类初始化方法
}
static void
vcam_source_init(VcamSource* d) { //实例初始化方法
}
int main(int argc, char** argv) {
GType dtype;
VcamSource* d;
dtype = vcam_source_get_type(); /* or d = T_TYPE_DOUBLE */
if (dtype)
g_print("Registration was a success. The type is %lx.\n", dtype);
else
g_print("Registration failed.\n");
return 0;
}
G_DEFINE_TYPE是在.c文件里出现的,他做了以下的工作:
- 声明了一个新类型的类的初始化函数,格式为<name space>_<name>_class_init 我们通常需要这个方法来注册属性、挂载虚函数。
- 声明了一个新类型的初始化函数,格式为 <name space>_<name>_init,我们通常在这个函数中初始化具体实例的信息。
- 声明了一个静态变量,让它指向新类型的类的父类。 格式为 <name space>_<name>_parent_class,可用于访问父类中的方法。
- 定义了获取类ing的函数,格式为<name space>_<name>_get_type (),这个正是我们注册类型需要的,所以GType vcam_source_get_type(void);这一行在头文件中就可以省略了。
G_DEFINE_TYPE很显然简化了我们的大量工作,但是要注意遵循对初始化函数的命名规范。
在面向对象的语言里,通常我们需要类型映射,需要检查某个实例是否试我们想要的类型,类似于:
source2 =VcamSoure(obj);
obj instanceof VcamSoure;
在Object里,同样使用宏定义来实现前两项:
#defind VCAM_SOURCE(obj) ....
#defind VCAM_IS_SOURCE(obj) ....
这有需要在.h加入很多行,于是为了更简洁, Gobject 提供了一个新的宏 G_DECLARE_FINAL_TYPE, 它可以做下面这些事情:
- 声明了一个<name space>_<name>_get_type ()方法,而G_DEFINE_TYPE包含了对它的定义,所以我们实际上跟本不需要写这个方法。
- 定义了一个有用的宏<NAME SPACE>_<NAME>,用来进行对象的映射。例如 VCAM_SOURCE(object)可以把普通对象类映射成我们需要的VcamSource类。
- 定义了一个<NAME SPACE>IS<NAME>, 用来判断是否一个实例来自于我们想要的类型。例如,VCAM_IS_SOURCE(obj), 可以判断 obj是否是VcamSource的实例。
于是,引入这个宏后,我们的.h文件就更简洁了:
#ifndef VCAM_SOURCE_H
#define VCAM_SOURCE_H
#include <glib-object.h>
#define VCAM_TYPE_SOURCE (vcam_source_get_type())
G_DECLARE_FINAL_TYPE(VcamSource, vcam_source, VCAM, SOURCE, GObject)
struct _VcamSoure {
GObject parent_instance;
};
struct _VcamSoureClass {
GObjectClass parent_class;
};
#endif /* __VCAM_SOURCE_H__ */
但是宏G_DECLARE_FINAL_TYPE有个缺陷,它不允许有任何子对象,相当于用Final修饰了VcamSoure 。如果需要有子对象,就需要使用另外一个宏G_DECLARE_DERIVABLE_TYPE 。