http://www.ibm.com/developerworks/cn/linux/l-gobject/

简单的说,GObject对象系统是一个建立在GLIB基础上的,用C语言完成的,具有跨平台特色的、灵活的、可扩展的、非常容易映射到其它语言的面向对象的框架。如果你是一个C语言的执着的追随者,你没有理由不研究一下它。

快速上手Gobject

http://blog.csdn.net/acs713/article/details/7778051

What is G-object?

—很多人被灌输了这样一种概念:要写面向对象程序,那么就需要学习一种面向对象编程语言,例如C++、Java、C#等等,而C语言是用来编写结构化程序的。
—事实上,面向对象只是一种编程思想,不是一种编程语言。换句话说,面向对象是一种游戏规则,它不是游戏。
—Gobject,亦称Glib对象系统,是一个程序库,它可以帮助我们使用C语言编写面向对象程序;它提供了一个通用的动态类型系统(GType)、一个基本类型的实现集(如整型、枚举等)、一个基本对象类型-Gobject、一个信号系统以及一个可扩展的参数/变量体系。

Why Bother to use Gobject?

—GObject告诉我们,使用C语言编写程序时,可以运用面向对象这种编程思想。
—Gobject系统提供了一个灵活的、可扩展的、并且容易映射到其他语言的面向对象的C语言框架。
—GObject的动态类型系统允许程序在运行时进行类型注册,它的最主要目的有两个:
)使用面向对象的设计方法来编程。GObject仅依赖于GLiblibc,通过它可使用纯C语言设计一整套面向对象的软件模块。
)多语言交互。在为已经使用
GObject框架写好的函数库建立多语言连结时,可以很容易对应到许多语言,包括C++、Java、Ruby、Python和.NET/Mono等。GObject被设计为可以直接使用在C
程序中,也封装至其他语言。   
 
透明的跨语言互通性
—Gobject如何解决静态语言与动态语言的沟通问题?
)作为一个

位的整型,并调用了function_foo函数。就

如你看到的,C函数的调用由gcc实现成了本地机器码的调用(这是实现起来最快的方法)。有了gcc这个第三方,我们的代码与

机器的沟通更顺畅了!记住:GType/GObject库不仅仅是为了设计向C开发者提供面向对象的特性,也是为了透明的跨语言互通性。

做一个受欢迎的协调者

—为了实现调用C函数,Python解释器需要做:

(1)找到函数所处的位置:这个意味着在C编译器编译成的二进制文件中寻找这个函数。

(2)在可执行的内存中,载入有关这个函数的相关代码。

(3)在调用这个函数前,将Python的参数转换为C兼容的参数。

(4)用正确的方式调用这个函数。

(5)将C函数的返回值转换成Python兼容的变量并将其返回至Python代码中。

—方案一:手动编写一些“粘合代码”,当每个函数被导入或导出时,使用这些代码将Python的参数转换为C兼容的参数,并将C的返回值转换为Python兼容的返回值。这个粘合代码将被连接到解释器上,从而解释器在解释Python程序时,可以完成程序中的调用C函数的工作。方案二:自动产生粘合代码,当每个函数被导入或导出时,使用一个特殊的编译器来读取原始的函数签名。

—GLib用的解决办法是,使用GType库来保存在当前运行环境中的所有由开发者描述的对象的描述。这些“动态类型”库将被特殊的“通用粘合代码”

来自动转换函数参数和进行函数调用在不同的运行环境之间。

GOBJECT模拟封装

在 GObject世界里,类是两个结构体的组合,一个是实例结构体,另一个是类结构体。有点绕。类、对象、实例有什么区别?可以这么理解,类-对象-实例,无非就是类型,该类型所声明的变量,变量所存储的内容。后面可以知道,类结构体初始化函数一般被调用一次,而实例结构体的初始化函数的调用次数等于对象实例化的次数。所有实例共享的数据,可保存在类结构体中,而所有对象私有的数据,则保存在实例结构体中。

GOBJCT如何模拟私有属性

—一种最简单的办法,是在类的定义时,只需要向结构体中添加一条注释,用于标明哪些成员是私有的,哪些是可以被直接访问的。C语言认为,程序员应当知道自己正在干什么,而且保证自己的所作所为是正确的。
—第二种办法,也就是最常用的办法,是把需要设为私有属性的数据再次封装,并且将该封装实例的定义放到实现.c文件中。在上页的例子中,GUPnPContextPrivate的定义就被定义为私有,其定义放在gupnp-context.c文件中
 

C语言实现CLASS域GOBJECT支持

如何实现gobject面向对象支持呢?

很简单,我们只需要建立自己的头文件,并添加一些宏定义G_DEFINE_TYPE即可。

这样,GUPnPContext就成为了Gobject库认可的一类合法公民了,即成功的把GUpnPContextClass类所代表的type(类型)注册到了glib类型系统中,并且将成功获取到一个类型ID。

也就是说,当你设计新类时,GUPnPContext可以被考虑加进你的继承体系,同时GUPnPContext也可以被用于组合成其他的类。

进一步理解GType类型系统

—Gtype类型系统是Glib运行时类型认证和管理系统。
—Gtype API是Gobject系统的基础,它提供注册和管理所有基本数据、用户定义对象和接口类型的技术实现。如:
G_DEFINE_TYPE宏、G_DEFINE_INTERFACE宏、g_type_register_static函数等都在GType实现。
—前面提到的G_DEFINE_TYPE宏,展开后主要用于实现用户定义类型,包括:声明类初始化函数、声明实例初始化函数、声明父类的一些信息、以及用于获取分配类型ID的xx_xx_get_type()函数;如下图所示:
 

GOBEJCT如何实现继承

—前面我们已经介绍,在
GObject世界里,类是两个结构体的组合,一个是实例结构体,另一个是类结构体。
—很容易理解,GOBJECT的继承需要实现实例结构体的继承和类结构体的继承。
—在前面的例子,我们通过在gupnpcontext实例中显示声明GSSDPClient parent来告知gobject系统GSSDPClient是gupnpcontext实例的双亲;同时,通过GUPnPContextClass定义中声明GSSDPClientClassparent_class。通过实例结构体和类结构体的共同声明,
—GOBJECT知道gupnpcontext是gssdpclient的子类。

GOBJECT构造函数

部分:类结构体初始化和实例结构体初始化。
类结构体初始化函数只被调用一次,而实例结构体的初始化函数的调用次数等于对象实例化的次数。这意味着,所有对象共享的数据,可保存在类结构体中,而所有对象私有的数据,则保存在实例结构体中。

多态的概念

—多态指同一个实体同时具有多种形式。它是面向对象程序设计的一个重要特征。
—把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
—赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。也就是说,父亲的行为像儿子,而不是儿子的行为像父亲。
—我们这里讨论的多态,主要指运行时多态,其具体引用的对象在运行时才能确定。

为什么要在GOBJECT引入多态?

—用C的struct可以实现对象。普通的结构体成员可以实现为成员数据,而对象的成员函数则可以由函数指针成员来实现。很多开源的软件也正是这么做的。
个实例,那么我们将浪费接近16KB的空间。
—很明显,我们不需要为每个实例保存这些指针,我们只需要保存一张包含这些指针的表。
Gobject如何实现多态?

)Gobject为每个子类在内存中保存了一份包含成员函数指针的表. 这个表,就是我们在C++经常说到的虚方法表(vtable)。当你想调用一个虚方法时,你必须先向系统请求查找这个对象所对应的虚方法表。这张表包含了一个由函数指针组成的结构体。在调用这些函数时,需要在运行时查找合适的函数指针,这样就能允许子类覆盖这个方法,我们称之为“虚函数”。

(2)  Gobject系统要求我们向它注册新声明的类型,系统同时要求我们去向它注册(对象的和类的)结构体构造和析构函数(以及其他的重要信息),这样系统就能正确的实例化我们的对象。

)Gobject系统通过枚举化所有的向它注册的类型来记录新的对象类型,并且要求所有实例化对象的第一个成员是一个指向它自己类的虚函数表的指针,每个虚函数表的第一个成员是它在系统中保存的枚举类型的数字表示。

由常用的g_object_new()想到的

—g_object_new能够为我们进行对象的实例化.所以它必然要知道对象对应的类的数据结构.
—如上图示例,除第一个参数外,很容易猜想后面的参数都是“属性名-属性值”的配对。
—第一个参数其实是一个宏:具体细节可以不去管它,可以知道它是去获取数据类型xx_xx_get_type函数的作用就是告诉它有关PMDList类的具体结构。在*.c文件实现中,G_DEFINE_TYPE宏可以为我们生成xx_xx_get_type函数的实现代码。
它可以帮助我们最终实现类类型的定义。 。 当g_object_new从xx_xx_get_type函数那里获取类类型标识码之后,便可以进行对象实例的内存分配及属性的初始化。初始化函数在前面已有介绍。
GOBJECT多态:将丑陋封锁在内部
—要想实现前面讲述的让g_object_new函数中通过“属性名-属性值”结构为Gobject子类对象的属性进行初始化,我们需要完成以下工作:

)实现xx_xx_set_property与xx_xx_get_property函数,完成g_object_new函数“属性名-属性值”结构向Gobject子类属性的映射;

(2)在Gobject子类的类结构体初始化函数中,让Gobject基类的两个函数指针set_property与get_property分别指向xx_xx_set_property与xx_xx_get_property。

)在Gobject子类的类结构体初始化函数中,为Gobject子类安装具体对象的私有属性。

可以看出,set_property是Gobject的虚函数实现,是运行时的多态。

GOBJECT多态:将优雅展示于外界

个函数指针指向它所期望的函数。
—类似的机制,在C++中被称为虚函数,主要用于实现多态。
—由于有了这种机制,我们可以使用g_object_new函数在对象实例化时便进行对象的初始化。
—当我们要获取或设置类的实例属性时,可直接使用统一的接口:g_object_get_propertyg_object_set_property
GOBJECT属性实现:泛型与多态

假设我们需要一种数据类型,可以实现一个可以容纳多类型元素的链表,我想为这个链表编写一些接口,可以不依赖于任何特定的类型,并且不需要我为每种数据类型声明一个多余的函数。这种接口必然能涵盖多种类型,我们称它为GValue(Generic Value,泛型)。

要编写一个泛型的属性设置机制,我们需要一个将其参数化的方法,以及与实例结构体中的成员变量名查重的机制。从外部上看,我们希望使用C字符串来区分属性和公有API,但是内部上来说,这样做会严重的影响效率。因此我们枚举化了属性,使用索引来标识它们。

属性规格,在Glib中被称作!GParamSpec,它保存了对象的gtype,对象的属性名称,属性枚举ID,属性默认值,边界值等,类型系统用!GParamSpec来将属性的字符串

名转换为枚举的属性ID,GParamSpec也是一个能把所有东西都粘在一起的大胶水。

gobject属性设置
 
—当我们需要设置或者获取一个属性的值时,传入属性的名字,并且带上GValue用来保存我们要设置的值,调用g_object_set/get_property。g_object_set_property函数将在GParamSpec中查找我们要设置的属性名称,查找我们对象的类,并且调用对象的set_property方法。这意味着如果我们要增加一个新的属性,就必须要覆盖默认的set/get_property方法。而且基类包含的属性将被它自己的方法所正常处理,因为GParamSpec就是从基类传递下来的。最后,应该记住,我们必须事先通过对象的class_init方法来传入GParamSpec参数,用于安装上属性!
 
Gobject消息系统:闭包

一个Closure是一个抽象的、通用表示的回调(callback)。它是一个包含三个对象的简单结构:

)一个函数指针(回调本身) ,原型类似于:

return_type function_callback (... , gpointeruser_data);

) user_data指针用来在调用Closure时传递到callback。

时,这个函数将被调用来释放Closure的结构。

一个GClosure提供以下简单的服务:

调用(g_closure_invoke):这就是Closure创建的目的: 它们隐藏了回调者的回调细节。

通知:相关事件的Closure通知监听者如Closure调用,Closure无效和Clsoure终结。监听者可以用注册g_closure_add_finalize_notifier(终结通知),g_closure_add_invalidate_notifier(无效通知)和g_closure_add_marshal_guards(调用通知)。

对于终结和无效事件来说,这是对等的函数(g_closure_remove_finalize_notifier和g_closure_remove_invalidate_notifier,但调用过程不是。

“一眼望穿”闭包

—GClosureMarshal是一个函数指针,但是要注意它是用来定义回调函数类型的而不是直接调用。
 
GObject中真正的回调是marshal_data,这个是一个void
*指针。这个在可通过查看C语言Marshaller的实现来得到证明。
Gobject为什么搞这么复杂?用于其它语言间的绑定.
 
闭包给多语言绑定带来了方便

个参数为GSignalMarshaller类型,它与前面体面提到的GClosureMarshal是一个东西,都是一个函数指针。

GSignalCMarshallerc_marshaller:该参数是一个GSignalCMarshall类型的函数指针,其值反映了回调函数的返回值类型和额外参数类型(所谓“额外参数”,即指除回调函数中instance和user_data以外的参数)。

例如,g_closure_marshal_VOID_VOID说明该signal的回调函数为以下的callback类型:

typedef  void (*callback)  (gpointer instance, gpointer 
user_data);

而g_closure_marshal_VOID_POINTER则说明该signal的回调函数为以下的callback类型:

typedef void (*callback)  (gpointer instance,gpointer 
arg1, gpointeruser_data);

GTypereturn_type:该参数的值应为回调函数的返回值在GType类型系统中的ID。

guintn_params:该参数的值应为回调函数的额外参数的个数。

...:这一系列的参数的值应为回调函数的额外参数在GType类型系统中的ID,且这一系列参数中第一个参数的值为回调函数的第一个额外参数在GType类型系统中的ID,依次类推。可以认为,信号就是包含对可以连接到信号的闭包的描述和对连接到信号的闭包的调用顺序的规定的集合体。

事实上,它是用来翻译闭包的参数和返回值类型的,它将翻译的结果传递给闭包。之所以不直接调用callback或闭包,而在外面加了一层msrshal的封装,主要是方便gobjec库与其他语言的绑定。例如,我们可以写一个pyg_closure_marshal_void_string函数,其中可以调用python语言编写的“闭包”并将其计算结果传递给Gvalue容器,然后再从Gvalue容器中提取计算结果。

Gobject消息系统:Signal机制

—在gobject系统中,信号是一种定制对象行为的手段,也是一种多种用途的通知机制。
—每一个信号都是和能发出信号的类型一起注册到系统中的。
—该类型的使用者,需要实现信号与闭包的连接,在给定的信号和给定的closure间指定对应关系,这样在信号被发射时,闭包会被调用。信号是closure被调用的主要机制;
—使用 GObject信号机制,一般有三个步骤:

)信号注册,主要解决信号与数据类型的关联问题

)信号连接,主要处理信号与闭包的连接问题;

)信号发射,   调用callback进行处理。

GObject对象系统的更多相关文章

  1. 从零构建JavaScript的对象系统

    一.正统的类与继承 类是对象的定义,而对象是类的实例(Instance).类不可直接使用,要想使用就必须在内存上生成该类的副本,这个副本就是对象. 以Java为例: public class Grou ...

  2. 深入了解Qt(二)之元对象系统(Meta-Object System)

    深入了解Qt主要内容来源于Inside Qt系列,本文做了部分删改,以便于理解.在此向原作者表示感谢! 在Qt Meta Object System-元对象系统这篇文章中,从底层实现的源码剖析了元对象 ...

  3. Qt Meta Object System-元对象系统

    研一的时候开始使用Qt,感觉用Qt开发图形界面比MFC的一套框架来方便的多.后来由于项目的需要,也没有再接触Qt了.现在要重新拾起来,于是要从基础学起. Now,开始学习Qt事件处理机制. 元对象系统 ...

  4. redis object 对象系统

    redis object对象系统 概述 redis 当中, sds字符串, adlist双向链表, dict字典, ziplist压缩链表, intset整数集合等均为底层数据结构 redis 并没有 ...

  5. php object 对象系统

    php object 对象系统 概述 本节内容仅谈论对象系统内容, 对于相关内容并不做更深一步的扩展, 相关扩展的内容会在后续补充 object 对象属于 zval 结构的一种形式 php 将所有执行 ...

  6. 基于类(Java)和基于原理(JavaScript)的对象系统的比较

    Java:面向对象编程语言,吸收了C++语言的各种优点,丢掉了C++让人头疼的多继承.指针等概念.具有功能强大和简单易用的两大特征.Java具有简单性.面向对象.分布式.健壮性.安全性.平台独立与可移 ...

  7. javascript中的null,对象系统还是非对象系统?

    1.一直以来的认知 在我学习js的过程中,爱民老师的绿皮书里将js的类型系统分成了两类: 其一是元类型系统:由typeof运算来检测 其二是对象类型系统:是元类型的object的一个分支 而null这 ...

  8. JS基础:基于原型的对象系统

    简介: 仅从设计模式的角度讲,如果我们想要创建一个对象,一种方法是先指定它的类型,然后通过这个类来创建对象,例如传统的面向对象编程语言 "C++"."Java" ...

  9. [Redis] redis的设计与实现-对象系统

    1.redis并没有直接使用前面的数据结构实现键值对数据库,而是基于数据结构创建了一个对象系统,字符串对象/列表对象/哈希对象/集合对象/有序集合对象都用到了至少一种前面的数据结构2.针对不同的使用场 ...

随机推荐

  1. firfox与about:config

    ¤什么是about:config¤about:config是Firefox的设置页面,Firefox提供了不少高级设置选项在这里以便让你可以更加详细地控制Firefox的运行方式.官方不推荐用户手工修 ...

  2. 转型(java)(.net)

    /** * 父类 */ class Animal { public void eat() { //输出 父类吃.... } } class Bird extends Animal { public v ...

  3. jdk的split 有多坑

    先看段 代码: String str = "4117|519951|长信利泰灵活配置混合型证券投资基金|长信利泰|3|3||||156|0||||||||||||||||||||{\&quo ...

  4. CodeForces 779A Pupils Redistribution

    简单题. 因为需要连边的人的个数一样,又要保证和一样,所以必须每个数字的个数都是一样的. #include<map> #include<set> #include<cti ...

  5. equals和hashcode 和 ==方法说明

    java中==.equals().hashCode()都和对象的比较有关,在java中这三者各有什么用处呢,即java中为什么需要设计这三种对象的比较方法呢? 关于== ==是容易理解的.java设计 ...

  6. WeGame导致WSL无法监听端口问题

    Windows 10 系统自带Linux子系统(WSL),可以方便的使用WSL运行Linux程序和脚本.笔者在WSL上运行Redis时突然发现无法监听6379端口,尝试重新安装WSL无果. 后来重新安 ...

  7. Java GlassPane进度条遮罩

    package com.swing.demo; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Flo ...

  8. 安卓 内容提供者 sql 区别

    韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha 内容提供者 用户只需关心 操作数据的url 就可以了. 实现 了 应用间 数据共享.可以操作数据 ...

  9. HDU5742 It's All In The Mind 数学思维题

    Problem Description Professor Zhang has a number sequence a1,a2,...,an. However, the sequence is not ...

  10. hdu 5755(Gauss 消元) &poj 2947

    Gambler Bo Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Tota ...