前言

本文可在https://paw5zx.github.io/GObject-tutorial-beginner-02/中阅读,体验更加

上一节中我们介绍了GObject类型的类和实例变量的创建和使用。GObject是一个基本的可实例化类类型,是所有使用GObject系统的类型的基类,提供了继承、封装、多态等面向对象的核心特性。不过我们一般不直接使用GObject本身,而是通过继承GObject来创建新的类型。

{% notel blue fa-circle-exclamation 注意:%}

对于GObject官方文档中:

可实例化的类类型:object(Instantiatable classed types: objects),本文以及后文中称其为类型。

不可实例化的类类型:interface(Non-instantiatable classed types: interfaces),本文以及后文中称其为接口。

本文中我们主要介绍一下GObject类型系统中的命名约定,以及介绍如何创建一个自定义的类型。

命名约定

{% note blue fa-circle-exclamation %}

命名约定贯穿整个GObject类型系统。

{% endnote %}

首先,我们先了解基本的命名约定:一个对象名由命名空间(也可称为moudle)和名称组成。例如,GObject由命名空间“G”和名称“Object”组成。GtkWidget由命名空间“Gtk”和名称“Widget”组成。在本文中,为了更好的演示,我们将定义一个新类型,其命名空间为“Paw”,名称为“Double”,用于表示一个浮点数据。

好了,你现在已了解了基本的命名约定,下面我们来看一下更多的命名约定:

  • 类型名称必须至少有三个字符长,并以a-z, A-Z或_开头。
  • 函数名使用object_method模式:要在类型为Double的实例上定义名为add的函数,则函数名为double_save
  • 使用前缀可以避免与其他项目的名称空间冲突。如我的库(或应用程序)名为Paw,就在所有函数名前加上paw_。例如:paw_double_add。前缀应该是一个词,即在第一个字母之后不应该包含任何大写字母。例如,应该是Exampleprefix而不是ExamplePrefix。

除了上述约定,本文还将介绍其他命名约定,将在文中适时描述。

类型创建和注册

类型的创建是指定义一个新的GObject类型,包括其所有的数据和行为。这会涉及到:

  • 定义类结构体和实例结构体
  • 定义类型相关的函数:如类初始化(class_init)、实例初始化(instance_init)等函数。

类型的注册是将创建的新GObject类型在GObject类型系统中注册,使其成为GObject系统可识别和使用的一部分。这会涉及到:

  • 调用类型注册函数:g_type_register_staticg_type_register_dynamic,这些函数负责在GObject类型系统中注册新创建的类型

在我们平时的使用中,类型的创建和注册通常可以通过GObject提供的一些便利宏来完成,如G_DECLARE_FINAL_TYPEG_DEFINE_TYPE等。这些便利宏可以让用户不用关心一些类型创建和注册的具体细节。

在本文中,为了更好地理解GObject类型系统,会先展示不使用便利宏的类型创建和注册过程,之后再介绍便利宏。

{% notel blue fa-circle-exclamation 注意:%}

GObject类型系统中有两种类型:静态类型和动态类型。

对于静态类型,即使所有实例都被销毁后也不会销毁其类。对于动态类型,在最后一个实例被销毁时销毁其类。GObject的类型是静态的,其派生的类型也是静态的。

手动

定义类型的类和实例结构体

类和实例结构体命名约定如下:

  • 类结构体的命名为:<Moudle><Name>Class,如PawDoubleClass。
  • 实例结构体的命名为:<Moudle><Name>,如PawDouble。
//类结构体
typedef struct _PawDoubleClass PawDoubleClass;
struct _PawDoubleClass
{
GObjectClass parent_class;
};

PawDoubleClass的第一个成员必须是其父类型的类结构体

//实例结构体
typedef struct _PawDouble PawDouble;
struct _PawDouble
{
GObject parent;
double value;
};

PawDouble的第一个成员必须是其父类型的实例结构体。

PawDouble有自己的成员value,是PawDouble类型代表的浮点数据的值

定义初始化函数

类和实例的初始化函数命名约定如下:

  • 类初始化函数的命名为:<moudle>_<name>_class_init,如paw_double_class_init。
  • 实例初始化函数的命名为:<moudle>_<name>_init,如paw_double_init。
//类构造函数
static void paw_double_class_init(PawDoubleClass* class)
{
} //实例构造函数
static void paw_double_init(PawDouble* self)
{
}

类型注册

对于静态类型,我们使用g_type_register_static将其注册至GObject的类型系统中

//file: gtype.h
GType
g_type_register_static (GType parent_type,
const gchar* type_name,
const GTypeInfo* info,
GTypeFlags flags);

其中:

  • parent_type:父类类型
  • type_name:类型名称,如PawDouble
  • info:向类型系统传递类型初始化和销毁的相关信息。GTypeInfo结构体将在下文中介绍
  • flags:如果类型是抽象类型或抽象值类型,则设置它们的标志位。否则,将其设为0。

GTyepInfo结构体:

typedef struct _GTypeInfo  GTypeInfo;

struct _GTypeInfo
{
/* interface types, classed types, instantiated types */
guint16 class_size; GBaseInitFunc base_init;
GBaseFinalizeFunc base_finalize; /* interface types, classed types, instantiated types */
GClassInitFunc class_init;
GClassFinalizeFunc class_finalize;
gconstpointer class_data; /* instantiated types */
guint16 instance_size;
guint16 n_preallocs;
GInstanceInitFunc instance_init; /* value handling */
const GTypeValueTable *value_table;
};

此结构必须在类型注册前创建,其中:

  • class_size: 类的大小。例如,PawDouble类型的类大小为sizeof(PawDoubleClass)
  • base_init, base_finalize: 这些函数初始化/销毁类的动态成员。在许多情况下,它们不是必需的,被赋值为NULL。详见GObject.BaseInitFuncGObject.ClassInitFunc
  • class_init: 类的初始化函数。用户需要将定义的类初始化函数赋值给class_init成员。按照命名约定,类初始化函数名称为<moudle>_<name>_class_init,例如paw_double_class_init
  • class_finalize: 类的销毁函数。因为GObject的子类类型是静态的,所以它没有销毁函数。将class_finalize成员赋值为NULL即可。
  • class_data: 用户提供数据,传递给类的初始化/销毁函数。通常赋值为 NULL。
  • instance_size: 实例的大小。例如,PawDouble类型的实例大小为sizeof(PawDouble)
  • n_preallocs: 用于指定预分配的实例数量。自GLib 2.10以来,该字段被忽略。
  • instance_init: 实例的初始化函数。用户需要将定义的实例初始化函数赋值给instance_init成员。按照命名约定,实例初始化函数名称为<moudle>_<name>_init,例如 paw_double_init
  • value_table: 这通常只对基本类型有用。如果类型是GObject的后代,赋值为NULL。

我们将编写一个函数paw_double_get_type,用于向GObject类型系统注册新类型,并返回所注册类型的id

GType paw_double_get_type(void)
{
static GType type = 0;
GTypeInfo info; if(type == 0)
{
//赋值
info.class_size = sizeof(PawDoubleClass);
info.base_init = NULL;
info.base_finalize = NULL;
info.class_init = (GClassInitFunc) paw_double_class_init;
info.class_finalize = NULL;
info.class_data = NULL;
info.instance_size = sizeof(PawDouble);
info.n_preallocs = 0;
info.instance_init = (GInstanceInitFunc) paw_double_init;
info.value_table = NULL;
//注册类型
type = g_type_register_static(G_TYPE_OBJECT, "PawDouble", &info, 0);
}
return type;
}

汇总

上述的类型创建和注册过程汇总如下:

#include <glib-object.h>

#define PAW_TYPE_DOUBLE  (paw_double_get_type())

//定义实例结构体
typedef struct _PawDouble PawDouble;
struct _PawDouble
{
GObject parent;
double value;
}; //定义类结构体
typedef struct _PawDoubleClass PawDoubleClass;
struct _PawDoubleClass
{
GObjectClass parent_class;
}; //类构造函数
static void paw_double_class_init(PawDoubleClass* class)
{
} //实例构造函数
static void paw_double_init(PawDouble* self)
{
} //首次调用时注册类型
//返回类型
GType paw_double_get_type(void)
{
static GType type = 0;
GTypeInfo info; if(type == 0)
{
info.class_size = sizeof(PawDoubleClass);
info.base_init = NULL;
info.base_finalize = NULL;
info.class_init = (GClassInitFunc) paw_double_class_init;
info.class_finalize = NULL;
info.class_data = NULL;
info.instance_size = sizeof(PawDouble);
info.n_preallocs = 0;
info.instance_init = (GInstanceInitFunc) paw_double_init;
info.value_table = NULL;
type = g_type_register_static(G_TYPE_OBJECT, "PawDouble", &info, 0);
}
return type;
} int main(int argc, char **argv)
{
GType dtype;
PawDouble* d; dtype = paw_double_get_type(); /* or dtype = PAW_TYPE_DOUBLE */
if(dtype)
g_print("Registration was a success. The type is %lx.\n", dtype);
else
g_print("Registration failed.\n"); d = g_object_new(PAW_TYPE_DOUBLE, NULL);
if(d)
g_print("Instantiation was a success. The instance address is %p.\n", d);
else
g_print("Instantiation failed.\n");
g_object_unref(d); /* Releases the object d. */ return 0;
}
  • 20-28:类初始化函数和实例初始化函数,参数class是指类结构体,参数self是指实例结构体。这两个函数在示例中内容为空,但他们是注册过程必须的。

  • 30-52:paw_double_get_type函数,返回PawDouble类型的id。这种函数的命名总是遵循<moudle>_<name>_get_type。同时,宏<MOUDLE>_TYPE_<NAME>(所有字符都是大写)是这个函数的别名。paw_double_get_type函数有一个静态变量type来存储对象的类型。在此函数的第一次调用时,type是零。然后函数内调用 g_type_register_static将新类型注册到类型系统中。在第二次和之后的调用中,该函数只是返回type,因为静态变量type已经被g_type_register_static赋予了非零值,并且它保持该值。

  • 39-49:设置info结构体并调用g_type_register_static注册。

  • 54-73:主函数。获取PawDouble类型id并显示它。函数g_object_new被用来创建类型实例。GObject API文档中表示g_object_new返回一个指向GObject实例的指针,但实际上它返回的是一个gpointergpointer基本等同于void*,可以被赋值给指向任何类型的指针。因此,语句d = g_object_new(PAW_TYPE_DOUBLE, NULL);是正确的。如果函数g_object_new返回的是GObject*,那么就需要对返回的指针进行类型转换。在创建完成后,我们打印了实例的地址。最后,使用函数g_object_unref释放并销毁实例。

剩余内容请前往https://paw5zx.github.io/GObject-tutorial-beginner-02/中阅读。

GObject学习笔记(二)类型创建与注册的更多相关文章

  1. python3.4学习笔记(二) 类型判断,异常处理,终止程序

    python3.4学习笔记(二) 类型判断,异常处理,终止程序,实例代码: #idle中按F5可以运行代码 #引入外部模块 import xxx #random模块,randint(开始数,结束数) ...

  2. InterSystems Ensemble学习笔记(二) Ensemble创建镜像, 实现自动故障转移

    系列目录 InterSystems Ensemble学习笔记(一) Ensemble介绍及安装InterSystems Ensemble学习笔记(二) Ensemble创建镜像, 实现自动故障转移 一 ...

  3. 【传智播客】Libevent学习笔记(二):创建event_base

    目录 00. 目录 01. 简介 02. 创建默认的event_base 03. 创建复杂的event_base 3.1 event_config_new函数 3.2 event_base_new_w ...

  4. MongoDB学习笔记二:创建、更新及删除文档

    插入并保存文档 对目标集使用insert方法插入一个文档: > db.foo.insert({"bar" : "baz"}) 这个操作会给文档增加一个&q ...

  5. twisted 学习笔记二:创建一个简单TCP客户端

    #coding=utf-8 from twisted.internet import reactor,protocol class QuickClient(protocol.Protocol): de ...

  6. ROS学习笔记二(创建ROS软件包)

    catkin软件包的组成 一个软件包必须满足如下条件才能被称之为catkin软件包: 必须包含一个catkin编译文件package.xml(manifests文件),此文件包含了描述该软件包的重要信 ...

  7. 微服务学习笔记二:Eureka服务注册发现

    Eureka服务注册发现 服务发现:云端负载均衡,一个基于 REST 的服务,用于定位服务,以实现云端的负载均衡和中间层服务器的故障转移. 1. Service Discovery: Eureka S ...

  8. JMX学习笔记(二)-Notification

    Notification通知,也可理解为消息,有通知,必然有发送通知的广播,JMX这里采用了一种订阅的方式,类似于观察者模式,注册一个观察者到广播里,当有通知时,广播通过调用观察者,逐一通知. 这里写 ...

  9. JDBC学习笔记二

    JDBC学习笔记二 4.execute()方法执行SQL语句 execute几乎可以执行任何SQL语句,当execute执行过SQL语句之后会返回一个布尔类型的值,代表是否返回了ResultSet对象 ...

  10. AJax 学习笔记二(onreadystatechange的作用)

    AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...

随机推荐

  1. Angular – CLI

    前言 一年半没有写 Angular 了. 现在又要开始写了. 复习过程中也顺便整理一下笔记呗. 介绍 CLI 是 Angular 的辅助工具. 输入一些 command 它会帮你 create 一些有 ...

  2. docker安装运行kafka单机版

    这里我们安装一下kafka的单机版,由于kafka是基于zk进行管理的,如果我们没有安装过zk的话,需要进行安装好zk再安装kafka,当然如果已经安装过了, 那就没必要安装了.我们可以执行docke ...

  3. 浏览器原生组件 shadow DOM(已完成)

    https://developer.mozilla.org/zh-CN/docs/Web/Web_Components/Using_shadow_DOM Web components 的一个重要属性是 ...

  4. 还在苦于密码太弱?教你3招用Linux生成高强度密码

    各位好啊,我是会编程的蜗牛,作为java开发者,我们平常肯定会接触Linux操作系统,其实除了一般的部署应用外,它还可以帮助我们生成密码.解决我们平常自己想各种复杂密码的烦恼,以后我会讲一讲如何安全地 ...

  5. 使用.NET并行任务库(TPL)与并行Linq(PLINQ)充分利用多核性能

    前言 最近比较闲,(项目要转Java被分到架构组,边缘化人员,无所事事 哈哈哈哈) 记录一下前段时间用到的.NET框架下采用并行策略充分利用多核CPU进行优化的一个方法 起因是项目中有个结算的方法,需 ...

  6. Win11使用Translucent TB设置Windows导航栏透明失败解决方案

    Win11使用Translucent TB设置Windows导航栏透明失败解决方案 Translucent TB下载方式:直接在Windows自带的Microsoft应用商店里面搜索下载就可以了 1. ...

  7. 快速搭建k8s

    换桥接模式,换sealos 桥接模式 部署出问题了,用这个: ipconfig 以太网适配器 以太网: 连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . ...

  8. 正态分布——“牛而B之”

    1 问题: 什么是正态分布,为什么这么出名和重要? 1.1 名气大 为什么叫"正态分布",也有地方叫"常态分布",这两个名字都不太直观,但如果我们各取一字变为& ...

  9. Power BI 网关无法添加My SQL数据集

    今天第一次发布数据类型为MySQL的数据集到Power BI报表服务器,desktop的连接正常,但是发布到web端后,添加网关时却提示以下错误,如下图所示: 错误信息: 无法创建连接,原因如下: 无 ...

  10. TypeError: add_triangle_mesh(): incompatible function arguments. The following argument types are supported: 问题终于解决了!!!!

    1 2024.10.12 14:52 Traceback (most recent call last): File "terrain_creation.py", line 119 ...