GObject学习笔记(一)类结构和实例

paw5zx Lv4

前言

最近阅读Aravis 源码,其中大量运用了GObject,于是打算学习一下。

此系列笔记主要面向初学者,不会很深入探讨源码的细节,专注于介绍GObject的基本用法。

此系列笔记参考GObject Tutorial for beginners

套个盾:文中定义的名词只是为了更好地理解GObject,不具备权威性。

本文中的GObject有两种常见含义,需要区分:

  • 有时它泛指GObject对象系统,也就是GLib提供的这一整套面向对象类型框架;
  • 有时它特指框架中的基类类型GObject,对应代码里的GObjectGObjectClassG_TYPE_OBJECT

后文如果说GObject中的类型系统、GObject框架等,指的是第一种含义;如果说GObject类型、GObject实例结构体、GObject类结构体等,指的是第二种含义。

类型

在介绍类结构和实例之前,先简单说明一下GObject中的“类型”这一概念。

在C语言中,我们通常会把struct _GObjectstruct _GObjectClass这样的结构体称为类型。但在GObject语境中,“类型”更多指的是由GType类型系统注册和管理的运行时类型信息。

每个GObject类型都会有一个对应的GType值作为类型标识符。例如,G_TYPE_OBJECT表示GObject基类类型。类型系统可以通过这个GType值找到该类型的注册信息,例如它的父类型、类结构大小、实例大小、类初始化函数、实例初始化函数等。

类和实例

在GObject中,每个可实例化类型通常都对应两个重要的C结构体定义:一个是类结构体(class structure),另一个是实例结构体(instance structure)。

  • 类结构体定义了类结构的内存布局,运行时的类结构保存类型共享的行为和元信息,例如虚函数入口、类型相关的配置以及其他由所有实例共享的类级信息(具体内容暂不展开)。
    • 类结构体会参与类型系统的注册与初始化过程(具体注册方式在下一节讨论)
    • g_object_new首次用于创建某一类型的实例时,类型系统会检查相应的类结构是否已经完成初始化,没有则创建并初始化。此后所有该类型的实例都将共享这个类结构。换句话说,每个类型通常只对应一个类结构。
  • 实例结构体定义了实例的内存布局,运行时的实例保存每个对象独有的状态数据。
    • 每次调用g_object_new创建实例时,都会分配一份新的内存,用来保存该实例自己的成员数据。因此,实例创建多少次,实例结构通常就会分配多少份。

注意:

这里说的“类结构体”和“实例结构体”,指的是结构体类型定义。在运行时,类型系统会根据这些结构体类型分配对应的内存。

为避免混淆,本系列文章约定:

  • 类结构体:指C代码中的类结构体类型定义,例如GObjectClass
  • 类结构:指运行时为某个类型实际分配出的类级内存,对应GObject语境中的class structure;
  • 实例结构体:指C代码中的实例结构体类型定义,例如GObject
  • 实例:指运行时为某个对象实际分配出的实例内存,对应GObject语境中的object instance。

下面以GObject这个具体类型为例,看看一个GObject类型的类结构体和实例结构体分别是什么样子。重点不是记住GObject本身的字段细节,而是借它理解GObject中类结构和实例的关系。

GObject实例结构体定义如下

1
2
3
4
5
6
7
8
9
10
//file: gobject.h
typedef struct _GObject GObject;
struct _GObject
{
GTypeInstance g_type_instance;

/*< private >*/
guint ref_count; /* (atomic) */
GData *qdata;
};

GObject类结构体定义如下(暂时不必关心其中的细节):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//file: gobject.h
typedef struct _GObjectClass GObjectClass;
struct _GObjectClass
{
GTypeClass g_type_class;

/*< private >*/
GSList *construct_properties;

/*< public >*/
/* seldom overridden */
GObject* (*constructor) (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties);
/* overridable methods */
void (*set_property) (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
void (*get_property) (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
void (*dispose) (GObject *object);
void (*finalize) (GObject *object);
/* seldom overridden */
void (*dispatch_properties_changed) (GObject *object,
guint n_pspecs,
GParamSpec **pspecs);
/* signals */
void (*notify) (GObject *object,
GParamSpec *pspec);

/* called when done constructing */
void (*constructed) (GObject *object);

/*< private >*/
gsize flags;

gsize n_construct_properties;

gpointer pspecs;
gsize n_pspecs;

/* padding */
gpointer pdummy[3];
};

下面使用一个简单示例,来说明GObject中类结构和实例之间的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//file: example01.c
#include <glib-object.h>

int main (int argc, char **argv)
{

GObject* instance1,* instance2; //指向实例的指针
GObjectClass* class1,* class2; //指向类结构的指针

instance1 = g_object_new (G_TYPE_OBJECT, NULL);
instance2 = g_object_new (G_TYPE_OBJECT, NULL);
g_print ("The address of instance1 is %p\n", instance1);
g_print ("The address of instance2 is %p\n", instance2);

class1 = G_OBJECT_GET_CLASS (instance1);
class2 = G_OBJECT_GET_CLASS (instance2);
g_print ("The address of the class of instance1 is %p\n", class1);
g_print ("The address of the class of instance2 is %p\n", class2);

g_object_unref (instance1);
g_object_unref (instance2);

return 0;
}

其中:

  • g_object_new函数创建实例并返回指向它的指针。在实例第一次被创建之前,它对应的类结构也会被创建并初始化。
  • 参数G_TYPE_OBJECT是GObject基类的类型标识符,这是GObject类型系统的核心,所有其他GObject类型都从这个基类型派生。
  • G_OBJECT_GET_CLASS返回指向参数所属类结构的指针
  • g_object_unref将引用计数减1,如果此时引用计数为0,则销毁实例。

输出:

1
2
3
4
The address of instance1 is 0x55d3ddc05600
The address of instance2 is 0x55d3ddc05620
The address of the class of instance1 is 0x55d3ddc05370
The address of the class of instance2 is 0x55d3ddc05370

可以发现,两个实例的地址不同,但两个实例对应的类结构地址相同,因为两个实例共享一个类结构

引用计数

这里不展开介绍引用计数的一般概念,只关注它在GObject中的基本表现。

在GObject中,GObject实例具有引用计数机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//file: example02.c
#include <glib-object.h>

static void show_ref_count (GObject* instance)
{
if (G_IS_OBJECT (instance))
/* Users should not use ref_count member in their program. */
/* This is only for demonstration. */
g_print ("Reference count is %d.\n", instance->ref_count);
else
g_print ("Instance is not GObject.\n");
}

int main (int argc, char **argv)
{
GObject* instance;

instance = g_object_new (G_TYPE_OBJECT, NULL);
g_print ("Call g_object_new.\n");
show_ref_count (instance);
g_object_ref (instance);
g_print ("Call g_object_ref.\n");
show_ref_count (instance);
g_object_unref (instance);
g_print ("Call g_object_unref.\n");
show_ref_count (instance);
g_object_unref (instance);
g_print ("Call g_object_unref.\n");
g_print ("Now the reference count is zero and the instance is destroyed.\n");
g_print ("The instance memories are possibly returned to the system.\n");
g_print ("Therefore, the access to the same address may cause a segmentation error.\n");

return 0;
}

其中:

  • g_object_new创建一个实例,然后将实例的引用计数置为1
  • g_object_ref将其引用计数加1
  • g_object_unref将引用计数减1,如果此时引用计数为0,则销毁实例。

输出:

1
2
3
4
5
6
7
8
9
10
Call g_object_new.
Reference count is 1.
Call g_object_ref.
Reference count is 2.
Call g_object_unref.
Reference count is 1.
Call g_object_unref.
Now the reference count is zero and the instance is destroyed.
The instance memories are possibly returned to the system.
Therefore, the access to the same address may cause a segmentation error.

初始化和析构过程

前面已经看到,一个GObject类型通常同时涉及两部分:类结构和实例。从运行时角度看,可以把它们的初始化和析构过程先粗略理解成下面这样

初始化

当某个类型的实例第一次被创建时,类型系统会先确保这个类型对应的类结构已经准备好;只有类结构初始化完成后,才会继续创建具体的实例。

依旧以本文一直在使用的GObject类型为例,在调用g_object_new(G_TYPE_OBJECT, NULL)时,可以粗略理解为:

    1. 先检查GObject对应的类结构是否已经初始化;
    1. 如果还没有,就先分配内存并初始化这份类结构;
    1. 在类结构准备好之后,为新的GObject实例分配内存;
    1. 最后完成GObject实例初始化。

注意:

GObject作为GObject系统提供的一个fundamental type,会被纳入GType类型系统进行注册,并以G_TYPE_OBJECT作为其类型标识。

同一类型的类结构通常只需要初始化一次。之后再次创建这个类型的实例时,一般不需要重复创建类结构,只需要为新实例分配并初始化它自己的内存。

析构

实例的销毁通常发生在引用计数降为0时,也就是调用g_object_unref后,不再有任何地方持有它的时候。

GObject类型是静态类型。静态类型永远不会破坏它的类结构。因此,即使被销毁的实例是最后一个,类结构仍然存在,直到程序终止。

附录

参考文章

1.GObject Tutorial for beginners

  • 标题: GObject学习笔记(一)类结构和实例
  • 作者: paw5zx
  • 创建于 : 2024-11-02 19:49:38
  • 更新于 : 2026-05-07 03:43:48
  • 链接: https://paw5zx.github.io/GObject-tutorial-beginner-01/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论