2. gobject中G_DEFINE_TYPE和g_object_new流程简介

C语言如何实现gobject面向对象支持呢?很简单,我们只需要建立自己的头文件,并在 .c 文件中添加一个宏定义G_DEFINE_TYPE即可。

 

G_DEFINE_TYPE 的流程(G_DEFINE_TYPE在 GLIB 的gobject/gtype.h 中定义):
#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() */

下面以gtk_gadget为例, 一般来说 gtk_表示命名空间,gadget表示对象名字.

/******** 首先在.h头文件中,会看到这样的语句 ***********/

GType gtk_gadget_get_type(void);

/******** 在对应的.c文件中就会有G_DEFINE_TYPE这个宏的声明 ***********/

G_DEFINE_TYPE(GtkGadget, gtk_gadget, G_TYPE_OBJECT)

G_DEFINE_TYPE 宏会声明一些函数, 并且实现了gtk_gadget_get_type(void),所以才需要在对应的.h头文件中声明gtk_gadget_get_type这个函数。下面这个例子来自于 gobject 的参考手册:

static void     gtk_gadget_init       (GtkGadget      *self);
static void     gtk_gadget_class_init (GtkGadgetClass *klass);
static gpointer gtk_gadget_parent_class = NULL;
static void     gtk_gadget_class_intern_init (gpointer klass)
{
  gtk_gadget_parent_class = g_type_class_peek_parent (klass);
  gtk_gadget_class_init ((GtkGadgetClass*) klass);
}

GType
gtk_gadget_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 (GTK_TYPE_WIDGET,
                                       g_intern_static_string ("GtkGadget"),
                                       sizeof (GtkGadgetClass),
                                       (GClassInitFunc) gtk_gadget_class_intern_init,
                                       sizeof (GtkGadget),
                                       (GInstanceInitFunc) gtk_gadget_init,
                                       0);
      {
        const GInterfaceInfo g_implement_interface_info = {
          (GInterfaceInitFunc) gtk_gadget_gizmo_init
        };
        g_type_add_interface_static (g_define_type_id, TYPE_GIZMO, &g_implement_interface_info);
      }
      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
    }
  return g_define_type_id__volatile;
}

从上面的代码中可以看出来,如果在.c文件中声明一个G_DEFINE_TYPE宏的话,就会自动生成一个gtk_gadget_get_type(void)函数,而这也就是需要在.h头文件中声明的原因,代码中, 最关键的部分是:

g_type_register_static_simple(GTK_TYPE_WIDGET,
                              g_intern_static_string ("GtkGadget"),
                              sizeof (GtkGadgetClass),
                              (GClassInitFunc) gtk_gadget_class_intern_init,
                              sizeof (GtkGadget),
                              (GInstanceInitFunc) gtk_gadget_init,
                              (GTypeFlags) flags);

g_type_register_static_simple 这个函数的作用, 就是将用户自己定义的类型注册到系统中, 从上面的代码我们还可以看出, G_DEFINE_TYPE 声明了两个函数, 但是并没有实现, 需要定义对象的用户自己去实现, 这两个函数是:


static void    gtk_gadget_init      (GtkGadget      *self);

static void     gtk_gadget_class_init (GtkGadgetClass*klass);

这两个函数是对象的初始化函数, 相当于C++中的构造函数, 第一个函数在每个对象创建的时候都会被调用, 第二个函数只有在第一次创建对象的时候才会被调用. 确切的说, 是调用 g_type_class_ref 时, 如果 class 没有初始化, 就会调用 gtk_gadget_class_init.

 

再来看看g_object_new函数的原型:

这个函数是个可变参数的函数, 第一个参数是需要创建的对象的类型, 当使用 g_object_new 来创建对象的时候, 这个参数是必须的,同时它还要求这个函数所创建的对象必须是GObject的子对象. 在我们定义自己的对象时, 必须要在系统中注册自己的类型, 这里的系统指的是 glib 的系统,即 glib 自己维护的一套数据结构. 先说一下第二个参数, 从第二个参数开始, 表示的是object 的属性, 它们都是成对出现的,如果没有属性需要在创建对象的时候设置, 则第二个参数设置成 NULL. 如果有属性要设置, 那么最后一个参数也要设置成 NULL. 一个来自于 gtk 的例子(gtk 中的对象, 都是继承自gobject):

/* 没有属性 */

g_object_new (GTK_TYPE_TOOL_ITEM, NULL);

/* 有一个属性 */

g_object_new (GTK_TYPE_TOOL_ITEM_GROUP, "label", label, NULL);

下面来看整个的调用过程, 一般调用的方式为:

g_object_new(gtk_gadget_get_type(), NULL);

这里我们假设没有属性需要设置. 在真实的代码中, 我们一般会定义一个宏来使用gtk_gadget_get_type():

#define GTK_TYPE_GADGET   (gtk_gadget_get_type())

所以一般的我们会写成:

g_object_new (GTK_TYPE_GADGET, NULL);

这样看上去更加的清晰.

GTK_TYPE_GADGET宏会取得这个对象的类型, 在 glib 中, 这个类型是一个唯一标示符. g_object_new 首先会确认 object_type (g_object_new 的第一个参数)是否是一个 gobject 对象.然后检查是否有属性值需要设置, 如果属性值为 NULL, 那么直接调用 g_object_newv. 如果需要设置属性值, 则调用 g_object_new_valist.

g_object_newv 的原型是:

gpointer
g_object_newv (GType       object_type,
               guint       n_parameters,
               GParameter *parameters)

这个函数的具体功能可以参考手册, g_object_new 是这样调用这个函数的:

g_object_newv(object_type,0, NULL);

g_object_newv 会调用 g_type_class_peek_static(object_type) 来取得 object_type的 class, 如果取回来的 class 为空, 那么就会调用g_type_class_ref(object_type), g_type_class_ref 会查找node, node 中会记录 class 相关信息, 如果 class 没有初始化,那么会调用type_class_init_Wm. type_class_init_Wm 会初始化 class, 如果在注册类型的时候指定了 class_init, 那么会调用 class_init. 接下来, 由于传递给 g_object_newv 的 n_parameters 和 parameters 都为空, 所以会直接调用 gobject class 的constructor 来构造对象. constructor 是 gobject class 的一个函数. 一般是 g_object_constructor, 用户可以创建自己的 constructor函数, 在自定义对象的 class_init 中将 GObject 中的 constructor, 改成自己需要的 constructor. 这样的行为类似于 C++ 中的虚函数. g_object_constructor 会调用g_type_create_instance 来创建对象实例(分配内存), g_type_create_instance 会调依次调用祖先对象的初始化函数,调用完成后再调用自己对象的初始化函数, 在这里就是 gtk_gadget_init.


  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
g_object_new_with_properties函数可以用于创建一个GObject实例,并且在实例化对象时可以设置该对象的属性值。它的原型如下: ```c gpointer g_object_new_with_properties (GType object_type, guint n_properties, GObjectConstructParam *properties); ``` 其object_type是要实例化对象的类型。n_properties是要设置的属性数量。properties是一个GObjectConstructParam类型的数组,用于保存要设置的属性信息。 下面是一个使用g_object_new_with_properties函数创建GObject实例并设置属性的示例代码: ```c #include <glib-object.h> typedef struct _MyObject MyObject; struct _MyObject { GObject parent_instance; gchar *name; gint age; }; enum { PROP_NAME = 1, PROP_AGE, N_PROPERTIES }; static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; static void my_object_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MyObject *self = MY_OBJECT (object); switch (property_id) { case PROP_NAME: g_free (self->name); self->name = g_value_dup_string (value); break; case PROP_AGE: self->age = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void my_object_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MyObject *self = MY_OBJECT (object); switch (property_id) { case PROP_NAME: g_value_set_string (value, self->name); break; case PROP_AGE: g_value_set_int (value, self->age); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void my_object_class_init (MyObjectClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->set_property = my_object_set_property; gobject_class->get_property = my_object_get_property; obj_properties[PROP_NAME] = g_param_spec_string ("name", "Name", "The name of the object", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); obj_properties[PROP_AGE] = g_param_spec_int ("age", "Age", "The age of the object", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); g_object_class_install_properties (gobject_class, N_PROPERTIES, obj_properties); } static void my_object_init (MyObject *self) { self->name = NULL; self->age = 0; } int main (void) { MyObject *obj = g_object_new_with_properties (MY_TYPE_OBJECT, 2, "name", "Tom", "age", 18); g_object_unref (obj); return 0; } ``` 在上面的示例代码,我们定义了一个MyObject类型的GObject子类,并且添加了两个属性:name和age。在类初始化时,我们使用g_object_class_install_properties函数将这两个属性安装到MyObject。在对象初始化时,我们将这两个属性的默认值分别设置为NULL和0。 在main函数,我们使用g_object_new_with_properties函数创建一个MyObject实例,并且设置name属性为"Tom",age属性为18。最后,我们使用g_object_unref函数释放该实例。 需要注意的是,在使用g_object_new_with_properties函数创建对象时,属性名称和属性值必须成对出现,并且属性名称必须是字符串类型。在示例代码,我们使用"name"和"age"作为属性名称。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值