📄 726.html
字号:
<br>
* 一个基本类型的实现集,如整型,枚举型和结构型等;<br>
* 一个基本对象体系之上的基本对象类型的实现的例子--GObject基本类型;<br>
* 一个信号系统,允许用户非常灵活的自定义虚的或重载对象的方法,并且能充当非常有效力的通知机制;<br>
* 一个可扩展的参数/变量体系,支持所有的能被用作处理对象属性或其它参数化类型的基本的类型。<br>
<br>
类型(GType)与对象(GObject)<br>
GLib中最有特色的是它的对象系统--GObject System,它是以Gtype为基础而实现的一套单根继承的C语言的面向对象的框架。<br>
<br>
GType 是GLib 运行时类型认证和管理系统。GType API 是GObject的基础系统,所以理解GType是理解GObject的关键。Gtype提供了注册和管理所有基本数据类型、用户定义对象和界面类型的技术实现。(注意:在运用任一GType和GObject函数之前必需运行g_type_init()函数来初始化类型系统。)<br>
<br>
为实现类型定制和注册这一目的,所有类型必需是静态的或动态的这二者之一。静态的类型永远不能在运行时加载或卸载,而动态的类型则可以。静态类型由 g_type_register_static()创建,通过GTypeInfo结构来取得类型的特殊信息。动态类型则由 g_type_register_dynamic()创建,用GTypePlugin结构来取代GTypeInfo,并且还包括 g_type_plugin_*()系列API。这些注册函数通常只运行一次,目的是取得它们返回的专有类的类型标识。<br>
<br>
还可以用g_type_register_fundamental来注册基础类型,它同时需要GTypeInfo和GTypeFundamentalInfo两个结构,事实上大多数情况下这是不必要的,因为系统预先定义的基础类型是优于用户自定义的。<br>
<br>
(本文重点介绍创建和使用静态的类型。)<br>
<br>
对象的定义<br>
在GObject系统中,对象由三个部分组成:<br>
<br>
1. 对象的ID标识(唯一,无符号长整型,所有此类对象共同的标识);<br>
2. 对象的类结构(唯一,结构型,由对象的所有实例共同拥有);<br>
3. 对象的实例(多个,结构型,对象的具体实现)。<br>
<br>
基于GObject的对象到底是什么样的呢?下面是基于GObject的简单对象 -- Boy的定义代码:<br>
<br>
<br>
<br>
<br>
/* boy.h */<br>
#ifndef __BOY_H__<br>
#define __BOY_H__<br>
<br>
#include <glib-object.h><br>
<br>
#define BOY_TYPE (boy_get_type())<br>
#define BOY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),BOY_TYPE,Boy))<br>
<br>
typedef struct _Boy Boy;<br>
typedef struct _BoyClass BoyClass;<br>
<br>
struct _Boy {<br>
GObject parent;<br>
//<br>
gint age;<br>
gchar *name;<br>
void (*cry)(void);<br>
};<br>
<br>
struct _BoyClass {<br>
GObjectClass parent_class;<br>
//<br>
void (*boy_born)(void);<br>
};<br>
<br>
GType boy_get_type(void);<br>
Boy* boy_new(void);<br>
int boy_get_age(Boy *boy);<br>
void boy_set_age(Boy *boy, int age);<br>
char* boy_get_name(Boy *boy);<br>
void boy_set_name(Boy *boy, char *name);<br>
Boy* boy_new_with_name(gchar *name);<br>
Boy* boy_new_with_age(gint age);<br>
Boy* boy_new_with_name_and_age(gchar *name, gint age);<br>
void boy_info(Boy *boy);<br>
<br>
#endif /* __BOY_H__*/<br>
<br>
这是一段典型的C语言头文件定义,包括编译预处理,宏定义,数据结构定义和函数声明;首先要看的是两个数据结构对象Boy和BoyClass,<br>
<br>
结构类型_Boy是Boy对象的实例,就是说我们每创建一个Boy对象,也就同时创建了一个Boy结构。Boy对象中的parent表示此对象的父类, GObject系统中所有对象的共同的根都是GObject类,所以这是必须的;其它的成员可以是公共的,这里包括表示年龄的age,表示名字的name 和表示方法的函数指针cry,外部代码可以操作或引用它们。<br>
<br>
结构类型_BoyClass是Boy对象的类结构,它是所有 Boy对象实例所共有的。BoyClass中的parent_class是GObjectClass,同GObject是所有对象的共有的根一样, GObejctClass是所有对象的类结构的根。在BoyClass中我们还定义了一个函数指针boy_born,也就是说这一函数指针也是所有Boy 对象实例共有的,所有的Boy实例都可以调用它;同样,如果需要的话,你也可以在类结构中定义其它数据成员。<br>
<br>
其余的函数定义包括三种,一种是取得Boy对象的类型ID的函数boy_get_type,这是必须有的;另一种是创建Boy对象实例的函数boy_new和 boy_new_with_*,这是非常清晰明了的创建对象的方式,当然你也可以用g_object_new函数来创建对象;第三种是设定或取得Boy对象属性成员的值的函数boy_get_*和boy_set_*。正常情况下这三种函数都是一个对象所必需的,另外一个函数boy_info用来显示此对象的当前状态。<br>
<br>
宏在GObject系统中用得相当广泛,也相当重要,这里我们定义了两个非常关键的宏,BOY_TYPE宏封装了boy_get_type函数,可以直接取得并替代Boy对象的ID标识;BOY(obj)宏是 G_TYPE_CHECK_INSTANCE_CAST宏的再一次封装,目的是将一个Gobject对象强制转换为Boy对象,这在对象的继承中十分关键,也经常用到。<br>
<br>
对象的实现<br>
下面的代码实现了上面的Boy对象的定义:<br>
<br>
<br>
<br>
<br>
/* boy.c */<br>
#include "boy.h"<br>
<br>
enum { BOY_BORN, LAST_SIGNAL };<br>
static gint boy_signals[LAST_SIGNAL] = { 0 };<br>
<br>
static void boy_cry (void);<br>
static void boy_born(void);<br>
<br>
static void boy_init(Boy *boy);<br>
static void boy_class_init(BoyClass *boyclass);<br>
<br>
GType boy_get_type(void)<br>
{<br>
static GType boy_type = 0;<br>
if(!boy_type)<br>
{<br>
static const GTypeInfo boy_info = {<br>
sizeof(BoyClass),<br>
NULL,NULL,<br>
(GClassInitFunc)boy_class_init,<br>
NULL,NULL,<br>
sizeof(Boy),<br>
0,<br>
(GInstanceInitFunc)boy_init<br>
};<br>
boy_type = g_type_register_static(G_TYPE_OBJECT,"Boy",&boy_info,0);<br>
}<br>
return boy_type;<br>
}<br>
<br>
static void boy_init(Boy *boy)<br>
{<br>
boy->age = 0;<br>
boy->name = "none";<br>
boy->cry = boy_cry;<br>
}<br>
<br>
static void boy_class_init(BoyClass *boyclass)<br>
{<br>
boyclass->boy_born = boy_born;<br>
boy_signals[BOY_BORN] = g_signal_new("boy_born",<br>
BOY_TYPE,<br>
G_SIGNAL_RUN_FIRST,<br>
G_STRUCT_OFFSET(BoyClass,boy_born),<br>
NULL,NULL,<br>
g_cclosure_marshal_VOID__VOID,<br>
G_TYPE_NONE, 0, NULL);<br>
}<br>
<br>
Boy *boy_new(void)<br>
{<br>
Boy *boy;<br>
boy = g_object_new(BOY_TYPE, NULL);<br>
g_signal_emit(boy,boy_signals[BOY_BORN],0);<br>
return boy;<br>
}<br>
<br>
int boy_get_age(Boy *boy)<br>
{<br>
return boy->age;<br>
}<br>
<br>
void boy_set_age(Boy *boy, int age)<br>
{<br>
boy->age = age;<br>
}<br>
<br>
char *boy_get_name(Boy *boy)<br>
{<br>
return boy->name;<br>
}<br>
<br>
void boy_set_name(Boy *boy, char *name)<br>
{<br>
boy->name = name;<br>
}<br>
<br>
Boy* boy_new_with_name(gchar *name)<br>
{<br>
Boy* boy;<br>
boy = boy_new();<br>
boy_set_name(boy, name);<br>
return boy;<br>
}<br>
<br>
Boy* boy_new_with_age(gint age)<br>
{<br>
Boy* boy;<br>
boy = boy_new();<br>
boy_set_age(boy, age);<br>
return boy;<br>
}<br>
<br>
Boy *boy_new_with_name_and_age(gchar *name, gint age)<br>
{<br>
Boy *boy;<br>
boy = boy_new();<br>
boy_set_name(boy,name);<br>
boy_set_age(boy,age);<br>
return boy;<br>
}<br>
<br>
static void boy_cry (void)<br>
{<br>
g_print("The Boy is crying ......");<br>
}<br>
<br>
static void boy_born(void)<br>
{<br>
g_print("Message : A boy was born .");<br>
}<br>
<br>
void boy_info(Boy *boy)<br>
{<br>
g_print("The Boy name is %s", boy->name);<br>
g_print("The Boy age is %d", boy->age);<br>
}<br>
<br>
在这段代码中,出现了实现Boy对象的关键函数,这是在Boy对象的定义中未出现的,也是没必要出现的。就是两个初始化函数,boy_init和 boy_class_init,它们分别用来初始化实例结构和类结构。它们并不被在代码中明显调用,关键是将其用宏转换为地址指针,然后赋值到 GTypeInfo结构中,然后由GType系统自行处理,同时将它们定义为静态的也是非常必要的。<br>
<br>
GTypeInfo结构中定义了对象的类型信息,包括以下内容:<br>
<br>
1. 包括类结构的长度(必需,即我们定义的BoyClass结构的长度);<br>
2. 基础初始化函数(base initialization function,可选);<br>
3. 基础结束化函数(base finalization function,可选);<br>
<br>
(以上两个函数可以对对象使用的内存来做分配和释放操作,使用时要用GBaseInitFunc和GBaseFinalizeFunc来转换为指针,本例中均未用到,故设为NULL。)<br>
4. 类初始化函数(即我们这里的boy_class_init函数,用GclassInit宏来转换,可选,仅用于类和实例类型);<br>
5. 类结束函数(可选);<br>
6. 实例初始化函数(可选,即我们这里的boy_init函数);<br>
7. 最后一个成员是GType变量表(可选)。<br>
<br>
定义好GTypeInfo结构后就可以用g_type_register_static函数来注册对象的类型了。<br>
<br>
g_type_register_static 函数用来注册对象的类型,它的第一个参数是表示此对象的父类的对象类型,我们这里是G_TYPE_OBJECT,这个宏用来表示GObject的父类;第二个参数表示此对象的名称,这里为"Boy";第三个参数是此对象的GTypeInfo结构型指针,这里赋值为&boyinfo;第四个参数是对象注册成功后返回此对象的整型ID标识。<br>
<br>
g_object_new函数,用来创建一个基于G_OBJECT的对象,它可以有多个参数,第一个参数是上面说到的已注册的对象标识ID;第二个参数表示后面参数的数量,如果为0,则没有第三个参数;第三个参数开始类型都是 GParameter类型,它也是一个结构型,定义为:<br>
<br>
<br>
<br>
<br>
struct GParameter{<br>
const gchar* name;<br>
GValue value;<br>
};<br>
<br>
关于GValue,它是变量类型的统一定义,它是基础的变量容器结构,用于封装变量的值和变量的类型,可以GOBJECT文档的GVALUE部分。<br>
<br>
信号的定义和应用<br>
<br>
在GObject系统中,信号是一种定制对象行为的手段,同时也是一种多种用途的通知机制。初学者可能是在GTK+中首先接触到信号这一概念的,事实上在普通的字符界面编程中也可以正常应用,这可能是很多初学者未曾想到的。<br>
<br>
一个对象可以没有信号,也可以有多个信号。当有一或多个信号时,信号的名称定义是必不可少的,此时C语言的枚举类型的功能就凸显出来了,用 LAST_SIGNAL来表示最后一个信号(不用实现的信号)是一种非常良好的编程风格。这里为Boy对象定义了一个信号BOY_BORN,在对象创建时发出,表示Boy对象诞生。<br>
<br>
同时还需要定义静态的整型指针数组来保存信号的标识,以便于下一步处理信号时使用。<br>
<br>
对象的类结构是所有对象的实例所共有的,我们将信号也定义在对象的类结构中,如此信号同样也是所有对象的实例所共有的,任意一个对象的实例都可以处理信号。因此我们有必要在在类初始化函数中创建信号(这也可能是GObject设计者的初衷)。函数g_signal_new用来创建一个新的信号,它的详细使用方法可以在GObject的API文档中找到。信号创建成功后,返回一个信号的标识ID,如此就可以用发射信号函数g_signal_emit向指定义对象的实例发射信号,从而执行相应的功能。<br>
<br>
本例中每创建一个新的Boy对象,就会发射一次BOY_BORN信号,也就会执行一次我们定义的boy_born函数,也就输出一行"Message : A boy was born ."信息。<br>
<br>
对象的属性和方法<br>
对象实例所有的属性和方法一般都定义在对象的实例结构中,属性定义为变量或变量指针,而方法则定义为函数指针,如此,我们一定要定义函数为static类型,当为函数指针赋值时,才能有效。<br>
<br>
对象的继承<br>
以下为继承自Boy对象的Man对象的实现,Man对象在Boy对象的基础上又增加了一个属性job和一个方法bye。<br>
<br>
<br>
<br>
<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -