一、使用GObject库模拟类的封装
1 子类结构体
本人GObject学习主要参考博客,讲的深入浅出,基本把我学习GStreamer遇见的基于GObject函数将明白了。
1.1 PM_Dlist类的头文件
/* file name : pm-dlist.h */
#ifndef PM_DLIST_H
#define PM_DLIST_H
#include<glib-object.h>
/* pm_dlist_get_type 函数的作用就是获取PMDList类的具体结构 */
#define PM_TYPE_DLIST (pm_dlist_get_type())
typedef struct _PMDListNode PMDListNode;
struct _PMDListNode{
PMDListNode *prev;
PMDListNode *next;
void *data;
};
/**
* GObject中,类是两个结构体的组合
* 一个是实例结构体
* 另一个是类结构提
*/
/**
* GObject结构体作为PMDList结构体第一个成员
* 这就意味着PMDList类继承自GObjext类
*/
/* 实例结构体 */
typedef struct _PMDList PMDList;
struct _PMDList{
GObject parent_instance;
PMDListNode *head;
PMDListNode *tail;
};
/* 类结构体 */
typedef struct _PMDListClass PMDListClass;
struct _PMDListClass{
GObjectClass parent_class;
};
GType pm_dlist_get_type(void);
#endif
我从前文提到的博客中学到,由于C语言没有为数据类型提供自定义命名空间的功能,为了避免所有程序文件中数据类型(包括函数)撞名。我们可以为这种命名方式取一个名字,叫做PT格式,P是项目名称的缩写,T是数据类型的名称。例如,在一个多面体建模(Polyhedron Modeling)的项目,定义一个双向链表的数据类型,通常pm-dlish.h。 对于struct double_list_XXX, enum PM_PROPERTY。常用typedef重定义数据类型。 PMDList和PMDListClass结构体第一个成员分别是GObject,GObjectClass,构成了一个继承于GObject类的PMList类。该类是两个结构体的组合,一个是实例结构体PMDList,另一个是类结构体PMDListClass。其中类结构体成员是所有对象公有的(类初始化代码只运行一次),实例结构体实例化一个对象(初始化代码运行一次)。
#define PM_TYPE_DLIST (pm_dlist_get_type())
GType pm_dlist_get_type(void);
由于对象的创建是由g_object_new(PM_TYPE_DLIST, NULL);函数完成的。既然创建的是PMDList类,这个函数的参数一定要包括PMDList类的数据结构,那么pm_dlist_get_type()函数就是告诉它有关PMList类的具体结构。pm_dlist_get_type函数的具体实现在下一节讲到。
1.2PM_Dlist类的具体实现
#include "pm-dlist.h"
G_DEFINE_TYPE (PMDList, pm_dlist, G_TYPE_OBJECT);
static
void pm_dlist_init (PMDList *self)
{
g_printf ("\t实例结构体初始化!\n");
self->head = NULL;
self->tail = NULL;
}
static
void pm_dlist_class_init (PMDListClass *klass)
{
g_printf ("类结构体初始化!\n");
}
在上述pm-dlist.c中,我们并没有看到pm_dlist_get_type函数的具体实现,这是因为GObject库所提供的G_DEFINE_TYPE宏可以为我们生成pm_dlist_get_type函数的实现代码。G_DEFINE_TYPE可以让GObject库的数据类型系统能够识别我们所定义的PMDList类,类的两个结构体名字必须是,PMDList和PMDListClass。例如G_DEFINE_TYPE (XXX, YYY G_TYPE_OBJECT);则类结构体必须是XXX和XXXClass。初始化函数必须是YYY_init和YYY_class_init。 G_TYPE_OBJECT指代g_object_get_type函数。 PM_TYPE_DLIST指代pm_dlist_get_type函数。 可以把PMDList类和GObject类这种形式的类类型称为PT类类型,并将pm_dlist_get_type和g_object_get_type这种形式的函数通称为p_t_get_type函数,并将PM_TYPE_DLIST和G_TYPE_OBJECT这样的宏称为P_TYPE_T宏。
总结 G_DEFINE_TYPE 依据提供的PMDlist、pm_dlist、G_TYPE_OBJECT参数自动生成p_t_get_type函数。 g_object_new函数进行对象实例化时,p_t_get_type便会被调用。 p_t_get_type函数的作用是向GObject库所提供的类型管理系统提供要注册: (1)PT类类型的相关信息(例如:PMDList,PMDListClass结构体) (2)PT类类型的实例结构体初始化函数(p_t_init) (3)PT类结构体初始化函数(p_t_class_init)
1.3 MAiN.C主函数
/* file name: main.c */
/**
* 为什么DList类一定要将GObject类作为父类?主要因为GObject类具有以下功能
* (1)基于引用计数的内存管理
* (2)对象的析造函数与析构函数
* (3)可设置对象属性的set/get函数
* (4)易于使用的信号机制
*/
#include"pm-dlist.h"
int
main(){
int i;
/* GObject 库的类型管理系统的初始化 */
g_type_init();
PMDList *list;/*类的实例化, 产生对象*/
/* 进行三次对象实例化 */
for(i = 0; i < 3; i++){
/*可以为对象的实例分配内存与初始化,并且将实例的引用计数设为1*/
list = g_object_new(PM_TYPE_DLIST, NULL);/* 创建对象的一个实例 */
/* 用于将对象的实例的引用计数减1,并检测对象的实例的引用计数是否为0,若为0,那么便释放对象的实例存储空间 */
g_object_unref(list);/* 销毁对象的一个实例 */
}
return 0;
}
编译
gcc pm-dlist.c main.c -o test `pkg-config --cflags --libs \
gobject-2.0` -I/home/lieryang/Desktop/GObject_Study/1.Imitate_calss
运行结果
类结构体初始化!
实例结构体初始化!
实例结构体初始化!
实例结构体初始化!
从输出结果可以看出,PMDList 类的类结构体初始化函数只被调用了一次,而实例结构体的初始化函数的调用次数等于对象实例化的次数。这意味着,所有实例共享的数据,可保存在类结构体中,而所有对象私有的数据,则保存在实例结构体中。 上述的 main 函数中,在使用 GObject 库的任何功能之前,必须先调用 g_type_init 函数初始化 GObject 库的类型管理系统,否则程序可能会出错。