GObject

#+SETUPFILE: ~/Dropbox/Doc/Org_Templates/level-1.org

创建一个类

定义一个类

/* file name: pm-dlist.h*/
#ifndef PM_DLIST_H
#define PM_DLIST_H

#include <glib-object.h>

#define PM_TYPE_DLIST (pm_dlist_get_type ())

typedef struct _PMDListNode PMDListNode;
struct  _PMDListNode {
        PMDListNode *prev;
        PMDListNode *next;
        void *data;
};

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 

在 GObject 世界里, 类是两个结构体的组合, 一个是实例结构体, 另一个是类结构体.

例如, PMDList 是实例结构体,PMDListClass 是类结构体, 它们合起来便可以称为 PMDList 类(此处的”PMDList 类”只是一个称谓, 并非是指 PMDList 实例结构体.

PMDList 类的实例结构体的第一个成员是 GObject 结构体, PMDList 类的类结构体的第一个成员是 GObjectClass 结构体. 其实, GObject 结构体与 GObjectClass 结构体分别是 GObject 类的实例结构体与类结构体, 它们分别作为 PMDList 类的实例结构体与类结构体的第一个成员时, 这意味着 PMDList 类继承自 GObject 类.

实现类

PMDList *list; /* 类的实例化 */
list = g_object_new (PM_TYPE_DLIST, NULL); /* 对象的实例化 */

pm_dlist_get_type 函数的作用就是告诉它有关 PMDList 类的具体结构, 实现在 C 文件里面.

实现类:

#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");
}

G_DEFINE_TYPE 宏, 顾名思义, 它可以帮助我们最终实现类类型的定义.

G_DEFINE_TYPE 让 GObject 库的数据类型系统能够识别我们所定义的 PMDList 类类型, 它接受三个参数:

  • 第一个参数是类名, 即 PMDList;

  • 第二个参数则是类的成员函数 (面向对象术语称之为”方法”或”行为”)名称的前缀, 例如 pm_dlist_get_type 函数即为 PMDList 类的一个成员函数, pm_dlist 是它的前缀;

  • 第三个参数则指明 PMDList 类类型的父类型为 G_TYPE_OBJECT

详细信息可以看测试代码

#include "test.h"

int main (void)
{
    /* GObject 库的类型管理系统的初始化 */
    g_type_init ();

    int i;
    PMDList *list;

    /* 进行三次对象实例化 */
    for (i = 0; i < 3; i++){
        list = g_object_new (PM_TYPE_DLIST, NULL);
        g_object_unref (list);
    }

    /* 检查实例是否为 GObject 对象 */
    list = g_object_new (PM_TYPE_DLIST, NULL);
    if (G_IS_OBJECT (list))
        g_printf ("\t这个实例是一个 GObject 对象!\n");

    return 0;
}

// gcc -o test test.c -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include  -lglib-2.0

上述测试程序的结果为:

类结构体初始化!
    实例结构体初始化!
    实例结构体初始化!
    实例结构体初始化!
    实例结构体初始化!
    这个实例是一个 GObject 对象!

意味着, 所有实例共享的数据, 可保存在类结构体中, 而所有对象私有的数据, 则保存在实例结构体中.

G_IS_OBJECT (list) 可以检测一个 list 对象是否为 G_TYPE_OBJECT 的对象

总结

为了便于描述, 我们可以将 PMDList 类和 GObject 类这种形式的类类型统称为 PT 类类型, 将 pm_dlist_get_typeg_object_get_type 这种形式的函数统称为 p_t_get_type 函数, 并将 PM_TYPE_DLISTG_TYPE_OBJECT 这样的宏统称为 P_TYPE_T 宏.

若想让 GObject 库能够识别你所定义的数据类型, 那么必须要提供一个 p_t_get_type 这样的函数. 虽然你不见得非要使用 p_t_get_type 这样的函数命名形式, 但是必须提 供一个具备同样功能的函数. p_t_get_type 函数的作用是向 GObject 库所提供的类 型管理系统提供要注册的 PT 类类型的相关信息, 其中包含 PT 类类型的实例结构体初 始化函数 p_t_init 与类结构体初始化函数 p_t_class_init, 例如上例中的 pm_list_initpm_list_class_init.

因为 p_t_get_type 函数是 g_object_new 函数的参数, 当我们首次调用 g_object_new 函数进行对象实例化时, p_t_get_type 函数便会被 g_object_new 函数调用, 从而引发 GObject 库的类型管理系统去接受 PT 类类型(例如 PMDList 类型)的申请并为其分配一个类型标识码作为 p_t_get_type 函数的返回值. 当 g_object_new 函数从 p_t_get_type 函数那里获取 PT 类类型标识码之后, 便可以进行对象实例的内存分配及属性的初始化.

GObject 的子类化需要以下几步:

  • 在 .h 文件中包含 glib-object.h
  • 在 .h 文件中构建实例结构体与类结构体, 并分别将 GObject 类的实例结构体与类结构体置于成员之首;
  • 在 .h 文件中定义 P_TYPE_T 宏, 并声明 p_t_get_type 函数;
  • 在 .c 文件中调用 G_DEFINE_TYPE 宏产生类型注册代码.

子类的私有属性

在头文件中定义

typedef struct _PMDList PMDList;
struct  _PMDList {
        GObject parent_instance;
};

在 C 文件中定义

typedef struct _PMDListPrivate PMDListPrivate;
struct  _PMDListPrivate {
        PMDListNode *head;
        PMDListNode *tail;
};
#define PM_DLIST_GET_PRIVATE(obj) (\
        G_TYPE_INSTANCE_GET_PRIVATE ((obj), PM_TYPE_DLIST, PMDListPrivate))
// 这个宏可以帮助我们从对象中获取所隐藏的私有属性. 例如, 在 PMDList
// 类的实例结构体初始化函数中, 使用 PM_DLIST_GET_PRIVATE 宏获取
// PMDList 对象的 head 与 tail 指针, 在 PMDList 类的实例结构体初始化函数中,
// 使用 PM_DLIST_GET_PRIVATE 宏获取 PMDList 对象的 head 与 tail 指针
static void
pm_dlist_init (PMDList *self)
{
        PMDListPrivate *priv = PM_DLIST_GET_PRIVATE (self);

        priv->head = NULL;
        priv->tail = NULL;
}

// 在以下代码中, PMDListPrivate 结构体是怎样被添加到 PMDList 对象中
static void
pm_dlist_class_init (PMDListClass *class)
{
        g_type_class_add_private (klass, sizeof (PMDListPrivate));
}

私有属性的外部访问

通过 g_object_new()

PMDList *list = g_object_new (PM_TYPE_DLIST,
                              "head", NULL,
                              "tail", NULL,
                              NULL);

为了实现上述功能, 需要:

  • 实现 p_t_set_propertyp_t_get_property 函数, 让它们来完成 g_object_new 函数的“属性名-属性值”结构向 GObject 子类属性的映射.
  • 在GObject 子类的类结构体初始化函数中, 让 GObject 类(基类)的两个函数指针 set_propertyget_property 分别指向 p_t_set_propertyp_t_get_property 函数.
  • 在 GObject 子类的类结构体初始化函数中, 为 GObject 子类安装属性.

前两个可以理解为 GObject 的两个虚函数,

static void
pm_dlist_class_init (PMDListClass *klass)
{
        /* 对象私有属性的安装,详见文档 */
        g_type_class_add_private (klass, sizeof (PMDListPrivate));


        GObjectClass *base_class = G_OBJECT_CLASS (klass);
        base_class->set_property = pm_dlist_set_property;
        base_class->get_property = pm_dlist_get_property;
        GParamSpec *pspec;
        pspec = g_param_spec_pointer ("head",
                                      "Head node",
                                      "The head node of the double list",
                                      G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT);
        g_object_class_install_property (base_class, PROPERTY_DLIST_HEAD, pspec);

        pspec = g_param_spec_pointer ("head-prev",
                                      "The previous node of the head node",
                                      "The previous node of the head node of the double list",
                                      G_PARAM_READABLE | G_PARAM_WRITABLE);
        g_object_class_install_property (base_class, PROPERTY_DLIST_HEAD_PREV, pspec);
        pspec = g_param_spec_pointer ("head-next",
                                      "The next node of the head node",
                                      "The next node of the head node of the double list",
                                      G_PARAM_READABLE | G_PARAM_WRITABLE);
        g_object_class_install_property (base_class, PROPERTY_DLIST_HEAD_NEXT, pspec);
        pspec = g_param_spec_pointer ("tail",
                                      "Tail node",
                                      "The tail node of the double list",
                                      G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT);
        g_object_class_install_property (base_class, PROPERTY_DLIST_TAIL, pspec);
        pspec = g_param_spec_pointer ("tail-prev",
                                      "The previous node of the tail node",
                                      "The previous node of the tail node of the double list",
                                      G_PARAM_READABLE | G_PARAM_WRITABLE);
        g_object_class_install_property (base_class, PROPERTY_DLIST_TAIL_PREV, pspec);
        pspec = g_param_spec_pointer ("tail-next",
                                      "The next node of the tail node",
                                      "The next node of the tail node of the double list",
                                      G_PARAM_READABLE | G_PARAM_WRITABLE);
        g_object_class_install_property (base_class, PROPERTY_DLIST_TAIL_NEXT, pspec);
}

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦