GObject学习笔记(一)类结构和实例
前言
最近阅读Aravis 源码,其中大量运用了GObject,于是打算学习一下。
此系列笔记主要面向初学者,不会很深入探讨源码的细节,专注于介绍GObject的基本用法。
此系列笔记参考GObject Tutorial for beginners
套个盾:文中定义的名词只是为了更好地理解GObject,不具备权威性。
本文中的GObject有两种常见含义,需要区分:
- 有时它泛指GObject对象系统,也就是GLib提供的这一整套面向对象类型框架;
- 有时它特指框架中的基类类型
GObject,对应代码里的GObject、GObjectClass和G_TYPE_OBJECT。
后文如果说GObject中的类型系统、GObject框架等,指的是第一种含义;如果说GObject类型、GObject实例结构体、GObject类结构体等,指的是第二种含义。
类型
在介绍类结构和实例之前,先简单说明一下GObject中的“类型”这一概念。
在C语言中,我们通常会把struct _GObject、struct _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 | //file: gobject.h |
GObject类结构体定义如下(暂时不必关心其中的细节):
1 | //file: gobject.h |
下面使用一个简单示例,来说明GObject中类结构和实例之间的关系
1 | //file: example01.c |
其中:
g_object_new函数创建实例并返回指向它的指针。在实例第一次被创建之前,它对应的类结构也会被创建并初始化。- 参数
G_TYPE_OBJECT是GObject基类的类型标识符,这是GObject类型系统的核心,所有其他GObject类型都从这个基类型派生。 - 宏
G_OBJECT_GET_CLASS返回指向参数所属类结构的指针 g_object_unref将引用计数减1,如果此时引用计数为0,则销毁实例。
输出:
1 | The address of instance1 is 0x55d3ddc05600 |
可以发现,两个实例的地址不同,但两个实例对应的类结构地址相同,因为两个实例共享一个类结构
引用计数
这里不展开介绍引用计数的一般概念,只关注它在GObject中的基本表现。
在GObject中,GObject实例具有引用计数机制:
1 | //file: example02.c |
其中:
g_object_new创建一个实例,然后将实例的引用计数置为1g_object_ref将其引用计数加1g_object_unref将引用计数减1,如果此时引用计数为0,则销毁实例。
输出:
1 | Call g_object_new. |
初始化和析构过程
前面已经看到,一个GObject类型通常同时涉及两部分:类结构和实例。从运行时角度看,可以把它们的初始化和析构过程先粗略理解成下面这样
初始化
当某个类型的实例第一次被创建时,类型系统会先确保这个类型对应的类结构已经准备好;只有类结构初始化完成后,才会继续创建具体的实例。
依旧以本文一直在使用的GObject类型为例,在调用g_object_new(G_TYPE_OBJECT, NULL)时,可以粗略理解为:
- 先检查GObject对应的类结构是否已经初始化;
- 如果还没有,就先分配内存并初始化这份类结构;
- 在类结构准备好之后,为新的GObject实例分配内存;
- 最后完成GObject实例初始化。
注意:
GObject作为GObject系统提供的一个fundamental type,会被纳入GType类型系统进行注册,并以G_TYPE_OBJECT作为其类型标识。
同一类型的类结构通常只需要初始化一次。之后再次创建这个类型的实例时,一般不需要重复创建类结构,只需要为新实例分配并初始化它自己的内存。
析构
实例的销毁通常发生在引用计数降为0时,也就是调用g_object_unref后,不再有任何地方持有它的时候。
GObject类型是静态类型。静态类型永远不会破坏它的类结构。因此,即使被销毁的实例是最后一个,类结构仍然存在,直到程序终止。
附录
参考文章
- 标题: 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 进行许可。