Chinaunix首页 | 论坛 | 博客
  • 博客访问: 114927
  • 博文数量: 27
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 85
  • 用 户 组: 普通用户
  • 注册时间: 2014-08-30 13:49
文章分类
文章存档

2017年(3)

2016年(1)

2015年(3)

2014年(20)

我的朋友

分类:

2016-03-18 15:29:14

定义 GObject 的子类

  • instance 结构:包含于类的实例相关的域,相当于 C++ 中的非静态公共成员。
  • class 结构:包含的域相当于 C++ 中的静态公共成员。
  • 私有成员在哪里定义?
与 C++ 不同,私有成员不是直接定义在类的声明中的(你甚至找不到一个到私有数据的指针)。GObject 的私有数据是在 class 结构初始化的时候,通过调用 g_typ_class_add_private 函数来指定的,这个函数只是指定私有数据的大小,类型系统在分配 instance 的时候会预留指定大小的空间供类实现作为私有数据使用。
  • 例子代码及注释(代码来自"Foundations of Gtk+ Development"一书第十一章)
/* myipaddress.h */
 
#include
#include
#include
 
G_BEGIN_DECLS
 
/* 这5个宏是定义 GObject 子类的“规定动作” */
#define MY_IP_ADDRESS_TYPE (my_ip_address_get_type ())
#define MY_IP_ADDRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_IP_ADDRESS_TYPE, MyIPAddress))
#define MY_IP_ADDRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MY_IP_ADDRESS_TYPE, MyIPAddressClass))
#define IS_MY_IP_ADDRESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_IP_ADDRESS_TYPE))
#define IS_MY_IP_ADDRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MY_IP_ADDRESS_TYPE))
 
typedef struct _MyIPAddress MyIPAddress;
typedef struct _MyIPAddressClass MyIPAddressClass;
 
/* instance 结构 */
struct _MyIPAddress
{
/* 父类的 instance 结构必须作为第一个域 */
GtkEntry entry;
};
 
/* class 结构 */
struct _MyIPAddressClass
{
GtkEntryClass parent_class;
 
/* “默认”信号处理函数 */
void (* ip_changed) (MyIPAddress *ipaddress);
};
 
/* 必须定义的一个函数,被上面的 MY_IP_ADDRESS_TYPE 宏使用,返回该类的 GType */
GType my_ip_address_get_type (void) G_GNUC_CONST;
/* 也可以不定义,只是一个 convenience,完全可以用 g_object_new (MY_IP_ADDRESS_TYPE) 来代替 */
GtkWidget* my_ip_address_new (void);
 
/* 对外提供两个接口函数 */
gchar* my_ip_address_get_address (MyIPAddress *ipaddress);
void my_ip_address_set_address (MyIPAddress *ipaddress, gint address[4]);
 
G_END_DECLS

注册 GObject 的子类

  • GObject 类型在使用之前必须在 GType 类型系统中注册,类型的注册工作在 xxx_get_type 函数中完成,例如上面例子中的 my_ip_address_get_type 函数。正如上面的例子所示,这个函数会在 new 一个该类的对象时被调用,如果该类型还没有被注册,则在第一次 new 这个类型的对象时注册。
/* myipaddress.c */
 
GType
my_ip_address_get_type (void)
{
static GType entry_type = 0;
 
if (!entry_type)
{
static const GTypeInfo entry_info =
{
sizeof (MyIPAddressClass),
NULL,
NULL,
(GClassInitFunc) my_ip_address_class_init,
NULL,
NULL,
sizeof (MyIPAddress),
0,
(GInstanceInitFunc) my_ip_address_init,
};
 
entry_type = g_type_register_static (GTK_TYPE_ENTRY, "MyIPAddress",
&entry_info, 0);
}
 
return entry_type;
}

不过上面的代码不是线程安全的。在 gtype.h 中(只要包含了 glib-object.h 就会自动包含这个文件)定义了一个方便的宏,它是线程安全的。这个宏是 #define G_DEFINE_TYPE(TN, t_n, T_P),不过它的实现只使用了 g_type_register_static_simple 函数(见下面),不够灵活,如果需要自己完整定义 GTypeInfo 结构的话,可以参考这个宏的实现。

  • 通过 GType API 在类型系统中注册一个类:
GType g_type_register_static		(GType			     parent_type,
const gchar *type_name,
const GTypeInfo *info,
GTypeFlags flags);
GType g_type_register_static_simple (GType parent_type,
const gchar *type_name,
guint class_size,
GClassInitFunc class_init,
guint instance_size,
GInstanceInitFunc instance_init,
GTypeFlags flags);
  • 类型的大部分信息保存在 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;
};

实现 GObject 的子类

实现子类的过程其实就是实现 GTypeInfo 结构中各个函数指针以及该类的接口函数的过程,因此首先需要了解 GTypeInfo 各个域的作用,在中还会进一步介绍这些函数被调用的顺序。

  • GObject 子类的 GTypeInfo 各个函数的作用
    • base_init:对 class 结构中继承过来的域进行设置。由于继承的域都是直接拷贝父类的 class 结构的,所以有些域可能需要进行“深拷贝”,这些操作就在这个函数完成。需要注意的是,假如 B 继承 A,是将 B 的 class 结构指针传给 A 所定义的 base_init 函数,因为只有 A 自身知道如何恰当设置相关的域。
    • base_finalize:与 base_init 相反。
    • class_init:这个函数对每个类只会调用一次,class 结构对每个类来说也只有一个副本。这个函数有两个作用,一是对 class 结构中的域进行初始化,二是注册该类的属性、信号、私有数据。
    • class_finalize:与 class_init 相反。
    • instance_init:初始化实例对象。
  • 例子代码及注释
static void
my_ip_address_class_init (MyIPAddressClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
/* Override the standard functions for setting and retrieving properties. */
gobject_class->set_property = my_ip_address_set_property;
gobject_class->get_property = my_ip_address_get_property;
 
/* Add MyIPAddressPrivate as a private data class of MyIPAddressClass. */
g_type_class_add_private (klass, sizeof (MyIPAddressPrivate));
 
/* Register the ip-changed signal, which will be emitted when the ip changes. */
my_ip_address_signals[CHANGED_SIGNAL] =
g_signal_new ("ip-changed", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (MyIPAddressClass, ip_changed),
NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
 
/* Register four GObject properties, one for each ip address number. */
g_object_class_install_property (gobject_class, PROP_IP1,
g_param_spec_int ("ip-number-1",
"IP Address Number 1",
"The first IP address number",
0, 255, 0,
G_PARAM_READWRITE));
 
g_object_class_install_property (gobject_class, PROP_IP2,
g_param_spec_int ("ip-number-2",
"IP Address Number 2",
"The second IP address number",
0, 255, 0,
G_PARAM_READWRITE));
 
g_object_class_install_property (gobject_class, PROP_IP3,
g_param_spec_int ("ip-number-3",
"IP Address Number 3",
"The third IP address number",
0, 255, 0,
G_PARAM_READWRITE));
 
g_object_class_install_property (gobject_class, PROP_IP4,
g_param_spec_int ("ip-number-4",
"IP Address Number 1",
"The fourth IP address number",
0, 255, 0,
G_PARAM_READWRITE));
}
 
static void
my_ip_address_init (MyIPAddress *ipaddress)
{
MyIPAddressPrivate *priv = MY_IP_ADDRESS_GET_PRIVATE (ipaddress);
PangoFontDescription *fd;
guint i;
 
for (i = 0; i < 4; i++)
priv->address[i] = 0;
 
fd = pango_font_description_from_string ("Monospace");
gtk_widget_modify_font (GTK_WIDGET (ipaddress), fd);
my_ip_address_render (ipaddress);
pango_font_description_free (fd);
 
/* The key-press-event signal will be used to filter out certain keys. We will
* also monitory the cursor-position property so it can be moved correctly. */

g_signal_connect (G_OBJECT (ipaddress), "key-press-event",
G_CALLBACK (my_ip_address_key_pressed), NULL);
g_signal_connect (G_OBJECT (ipaddress), "notify::cursor-position",
G_CALLBACK (my_ip_address_move_cursor), NULL);
}
阅读(1476) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~