官网API:https://developer.gnome.org/gobject/stable/
本文是学习学习他人的博客的心得(具体详见“楼主见解”),如果源网站可访问的话,建议直接访问源网站:
楼主见解:
主要讲解接口是如何实现的。
整理一下常用宏:
继承常用宏:其中P表示项目名称,T表示类名称,PTPrivate表示私有数据结构体。
#define P_TYPE_T (p_t_get_type ())
#define P_T(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), P_TYPE_T, PT))
#define P_IS_T(obj) G_TYPE_CHECK_INSTANCE_TYPE ((obj), P_TYPE_T))
#define P_T_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), P_TYPE_T, PTClass))
#define P_IS_T_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), P_TYPE_T))
#define P_T_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), P_TYPE_T, PTClass))
#define P_T_GET_PRIVATE(obj) (\
G_TYPE_INSTANCE_GET_PRIVATE ((obj), P_TYPE_T, PTPrivate))
- P_TYPE_T:仅在使用 g_object_new 进行对象实例化的时候使用一次,用于向 GObject 库的类型系统注册 PT 类;
- P_T (obj):用于将 obj 对象的类型强制转换为 P_T 类的实例结构体类型;
- P_IS_T (obj):用于判断 obj 对象的类型是否为 P_T 类的实例结构体类型;
- P_T_CLASS(klass):用于将 klass 类结构体的类型强制转换为 P_T 类的类结构体类型;
- P_IS_T_CLASS(klass):用于判断 klass 类结构体的类型是否为 P_T 类的类结构体类型;
- P_T_GET_CLASS(obj):获取 obj 对象对应的类结构体类型。
- P_T_GET_PRIVATE(obj):获取obj对象对应的私有数据。
接口常用宏:其中P表示项目名称,T表示类名称,I是接口的缩写
#define P_TYPE_IT (p_t_get_type ())
#define P_IT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), P_TYPE_IT, PIt))
#define P_IS_IT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), P_TYPE_IT))
#define P_IT_GET_INTERFACE(obj) \
(G_TYPE_INSTANCE_GET_INTERFACE ((obj), P_TYPE_IT, PItInterface))
- P_TYPE_IT:仅在接口实现时使用一次,用于向 GObject 库的类型系统注册 PIT 接口;
- P_IT (obj):用于将 obj 对象的类型强制转换为 P_IT 接口的实例结构体类型;
- P_IS_IT (obj):用于判断 obj 对象的类型是否为 P_IT 接口的实例结构体类型;
- P_IT_GET_INTERFACE(obj):获取 obj 对象对应的 P_IT 接口的类结构体类型。
接口的定义:
第一:.h实现
#ifndef MY_IUSB_H
#define MY_IUSB_H
#include <glib-object.h>
/*
写之前需要确定的三个名称如下(其中I表示接口):
类名称:MyIUse;对应的接口类(结构类)MyIUserInterface(MyIUserClass)
类型:MY_TYPE_IUSE(一般定义取类名的大写,中间加TYPE组合)
函数前缀:my_iuse(一般定义取类名的小写,中间加下划线组合)
*/
#define MY_TYPE_IUSB (my_iusb_get_type ())
#define MY_IUSB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),MY_TYPE_IUSB, MyIUsb))
#define MY_IS_IUSB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_IUSB))
#define MY_IUSB_GET_INTERFACE(obj) (\
G_TYPE_INSTANCE_GET_INTERFACE ((obj), MY_TYPE_IUSB, MyIUsbInterface))
//类结构无需具体是实现
typedef struct _MyIUsb MyIUsb;
typedef struct _MyIUsbInterface MyIUsbInterface;
struct _MyIUsbInterface {
GTypeInterface parent_interface;
//接口
gchar * (*read) (MyIUsb *self);
void (*write) (MyIUsb *self, const gchar *str);
};
GType my_iusb_get_type (void);
//接口定义
gchar * my_iusb_read (MyIUsb *self);
void my_iusb_write (MyIUsb *self, const gchar *str);
#endif
第二:.c实现
#include "my-iusb.h"
G_DEFINE_INTERFACE (MyIUsb, my_iusb, G_TYPE_INVALID);
static void
my_iusb_default_init (MyIUsbInterface *iface)
{
}
gchar *
my_iusb_read (MyIUsb *self)
{
g_return_if_fail (MY_IS_IUSB (self));
MY_IUSB_GET_INTERFACE (self)->read (self);
}
void
my_iusb_write (MyIUsb *self, const gchar *str)
{
g_return_if_fail (MY_IS_IUSB (self));
MY_IUSB_GET_INTERFACE (self)->write (self, str);
}
接口的实现类:
同样继承自GObject,基本和GObject继承一致,唯一的区别就是在.c文件中用G_DEFINE_TYPE_WITH_CODE代替G_DEFINE_TYPE,其中前者比后者参数多一个,这个就是接口实现。多一个接口初始化函数,此函数my_iusb_interface_init,此函数中,将接口的函数指针,指向实际执行的函数。这样就可以通过接口调用之类的接口了。
第一:.h实现
#ifndef MY_UDISK_H
#define MY_UDISK_H
#include "my-iusb.h"
#define MY_TYPE_UDISK (my_udisk_get_type ())
#define MY_UDISK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_UDISK, MyUdisk))
#define MY_IS_UDISK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_UDISK))
#define MY_UDISK_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), MY_TYPE_UDISK, MyUdiskClass))
#define MY_IS_UDISK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MY_TYPE_UDISK))
#define MY_UDISK_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj),MY_TYPE_UDISK,MyUdiskClass))
typedef struct _MyUdisk MyUdisk;
typedef struct _MyUdiskClass MyUdiskClass;
struct _MyUdisk {
GObject parent;
GString *data;
};
struct _MyUdiskClass {
GObjectClass parent_class;
};
GType my_udisk_get_type (void);
#endif
第二:.c实现
#include "my-udisk.h"
static void my_iusb_interface_init (MyIUsbInterface *iface);
G_DEFINE_TYPE_WITH_CODE (MyUdisk, my_udisk, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (MY_TYPE_IUSB, my_iusb_interface_init));
static gchar *
my_udisk_read (MyIUsb *iusb)
{
MyUdisk *udisk = MY_UDISK (iusb);
return udisk->data->str;
}
static void
my_udisk_write (MyIUsb *iusb, const gchar *str)
{
MyUdisk *udisk = MY_UDISK (iusb);
g_string_assign (udisk->data, str);
}
static void
my_udisk_init (MyUdisk *self)
{
self->data = g_string_new (NULL);
}
static void
my_udisk_class_init (MyUdiskClass *self)
{
}
static void
my_iusb_interface_init (MyIUsbInterface *iface)
{
iface->read = my_udisk_read;
iface->write = my_udisk_write;
}
第三:测试代码
#include "my-udisk.h"
int
main (void)
{
g_type_init ();
MyUdisk *udisk = g_object_new (MY_TYPE_UDISK, NULL);
my_iusb_write (MY_IUSB (udisk), "I am u-disk!");
gchar *data = my_iusb_read (MY_IUSB (udisk));
g_printf ("%s\n\n", data);
g_printf ("Is udisk a MyIUsb object?\n");
if (MY_IS_IUSB (udisk))
g_printf ("Yes!\n");
else
g_printf ("No!\n");
g_printf ("\nIs udisk a MyUdisk object?\n");
if (MY_IS_UDISK (udisk))
g_printf ("Yes!\n");
else
g_printf ("No!\n");
return 0;
}
源博客网址:http://garfileo.is-programmer.com/categories/6934/posts
GObject 对接口的模拟
在文档 [1] 中谈到接口古已有之,但是类的继承赋予了它一些新的概念。本文结合实例,学习如何使用 GObject 库所提供的接口类型来表达这些概念。
接口声明
下面的代码(文件名 my-iusb.h)声明了一个叫做 MyIUsb 的接口,My 是项目名,I 是 interface 的首字母的大写,Usb 表示接口的名称。MyIUsb 就表示在“My”项目里,Usb 是一个 Interface。
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 |
|
上述代码与文档[5]中 KbBibtex 类的声明代码很相似,但也有所区别。
首先,MyIUsb 接口的实例结构体,它只是个名字,并没有具体实现。这是因为,在 Java 那样的语言里谈到“接口”,那么则意味着它是无法实例化的。这其中是有一定的道理的,因为接口只是协议嘛。
其次,MyIUsbInterface 是 MyIUsb 接口的类结构体,它继承自 GTypeInterface 类结构体。也就是说,当你要声明接口时,那么接口的类结构体便要继承 GTypeInterface 类结构体,而当你声明的是可实例化为对象的类时,其类结构体便要继承 GObjectClass。
再次,在 MyIUsbInterface 结构体中,可包含一组函数指针,它们便是接口的协议。
最后,声明接口。对于本例而言,接口便是 my_iusb_read 与 my_iusb_write。
上述代码的解读过程大致是:
- 这是一个 usb 接口。
- 这个接口即可以 read,也可以 write,但是 read 只能是返回字符串,而 write 只能是接受字符串
- 声明这个接口的具体形式,在现实中则以理解为声明 usb 接口有几根线构成,每根线的功能等等。
接口的定义
建立 my-iusb.c 源文件,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
其中,my_iusb_default_init 是 G_DEFINE_INTERFACE 宏的展开代码中声明的一个函数,其中可以放置接口的一些初始化代码。如果没有这方面的需求,就让它表现为一个空函数即可,否则编译器会警告你,说你有一个函数声明了但没有实现。
另外,上述代码中出现了三个陌生的宏,其功能如下:
- G_DEFINE_INTERFACE 宏的功用与 G_DEFINE_TYPE 类似,后者在 GObject 子类化的时候经常使用;
- MY_IS_IUSB 宏是用来检测对象是否为 MyIUsb 类型,最好要在 my-iusb.h 中进行定义,代码为:
1 |
|
- 在 GObject 子类化的时候,也可以定义类似的宏,用于识别某个对象对应哪种类型。比如之前我们用过的一个 G_IS_OBJECT 宏,它可以识别对象是否为 GObject 类型的对象。
- MY_IUSB_GET_INTERFACE 宏,用于从 MyIUsb 接口的实例结构体中取出类结构体指针,然后利用该指针访问接口对应的方法。至于 MyIUsb 接口的实例结构体是怎样与类结构体指针取得关联的,那是 GObject 的内幕,暂且不必关心。MY_IUSB_GET_INTERFACE 宏也需要在 my-iusb.h 中进行定义,如下:
1 2 |
|
现在,将 my-iusb.h 文件更新为:
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 |
|
插曲:经常要用到并且需要自己定义的宏
文档 [2-5] 中自定义了多个宏,在此略微进行总结一下,免的后续文档再多费口舌。
对于 GObject 的子类化,那么在声明类的时候,在头文件中直接插入类似下面的一组宏定义:
1 2 3 4 5 6 |
|
这些宏的用法总结如下:
- P_TYPE_T:仅在使用 g_object_new 进行对象实例化的时候使用一次,用于向 GObject 库的类型系统注册 PT 类;
- P_T (obj):用于将 obj 对象的类型强制转换为 P_T 类的实例结构体类型;
- P_IS_T (obj):用于判断 obj 对象的类型是否为 P_T 类的实例结构体类型;
- P_T_CLASS(klass):用于将 klass 类结构体的类型强制转换为 P_T 类的类结构体类型;
- P_IS_T_CLASS(klass):用于判断 klass 类结构体的类型是否为 P_T 类的类结构体类型;
- P_T_GET_CLASS(obj):获取 obj 对象对应的类结构体类型。
对于 GTypeInterface 的子类化,在声明类的时候,在头文件中直接插入类似下面的一组宏定义:
1 2 3 4 5 |
|
- P_TYPE_IT:仅在接口实现时使用一次,用于向 GObject 库的类型系统注册 PIT 接口;
- P_IT (obj):用于将 obj 对象的类型强制转换为 P_IT 接口的实例结构体类型;
- P_IS_IT (obj):用于判断 obj 对象的类型是否为 P_IT 接口的实例结构体类型;
- P_IT_GET_INTERFACE(obj):获取 obj 对象对应的 P_IT 接口的类结构体类型。
接口的实现——制造 U 盘
既然已经有了 USB 接口的声明(协议),那就意味着我们可以制造具备这种接口的类与对象了。
首先声明 U 盘类(my-udisk.h):
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 |
|
上述代码声明了一个 MyUdisk 类,它是 GObject 的子类。MyUdisk 类的实例结构体中有一个 GString 类型的 data 属性,用于存储数据。也就是说,这个 U 盘设计的有些脑残,因为它只能存储一个字符串!
然后定义 U 盘类(my-udisk.c):
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 |
|
上述代码中,有几处需要留意的地方:
- my_iusb_interface_init 函数声明必须要放在 G_DEFINE_TYPE_WITH_CODE 宏之前,因为这个宏的展开代码中需要使用这个函数;
- G_DEFINE_TYPE_WITH_CODE 是文档 [2-5] 中出现过的 G_DEFINE_TYPE 宏的“扩展版本”,在本例中可以向 my_udisk_get_type 函数(即 MY_TYPE_UDISK 宏展开的那个函数)中插入 C 代码。在本例中,这个宏所插入的 C 代码是“G_IMPLEMENT_INTERFACE(MY_TYPE_IUSB,my_iusb_interface_init)”,其中 G_IMPLEMENT_INTERFACE 宏的作用是将接口添加到 MyUdisk 类中;
- my_iusb_interface_init 函数的作用是表明 MyUdisk 类实现了 MyIUsb 所规定的接口。
至此,MyIUsb 接口的一个实现便完成了。下面的 main.c 文件中的代码用于测试这个 U 盘是否可以使用 MyIUsb 接口进行访问,即:
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 |
|
这个程序的编译命令及执行结果如下:
1 2 3 4 5 6 7 8 9 |
|
一切都在掌握之中啊。
还可以接着造移动硬盘
可以像制造 U 盘那样,便可以炮制移动硬盘了。这项艰巨的任务便交给你了,而懒惰的我只提供下面这个计算机主机程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
~ End ~
参考文档
[1] 继承与接口
[3] GObject 子类私有属性模拟
[5] 温故而知新
转载时,希望不要链接文中图片,另外请保留本文原始出处:http://garfileo.is-programmer.com