[转] 定义、注册和实现 GObject 类的子类

定义 GObject 的子类

·    instance 结构:包含于类的实例相关的域,相当于 C++ 中的非静态公共成员。

·    class 结构:包含的域相当于 C++ 中的静态公共成员。

·    私有成员在哪里定义?

C++ 不同,私有成员不是直接定义在类的声明中的(你甚至找不到一个到私有数据的指针)。GObject 的私有数据是在 class 结构初始化的时候,通过调用 g_typ_class_add_private 函数来指定的,这个函数只是指定私有数据的大小,类型系统在分配 instance 的时候会预留指定大小的空间供类实现作为私有数据使用。

·    例子代码及注释(代码来自"Foundations of Gtk+ Development"一书第十一章)

#include <glib.h>

#include <glib-object.h>

#include <gtk/gtkentry.h>  

G_BEGIN_DECLS  

#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;  

 

struct _MyIPAddress

{

GtkEntry entry;

};  

struct _MyIPAddressClass

{

GtkEntryClass parent_class;  

void (* ip_changed) (MyIPAddress *ipaddress);

};  

 

GType my_ip_address_get_type (void) G_GNUC_CONST;

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 这个类型的对象时注册。

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

{

guint16 class_size;  

GBaseInitFunc base_init;

GBaseFinalizeFunc base_finalize;  

GClassInitFunc class_init;

GClassFinalizeFunc class_finalize;

gconstpointer class_data;  

guint16 instance_size;

guint16 n_preallocs;

GInstanceInitFunc instance_init;  

const GTypeValueTable *value_table;

};

实现 GObject 的子类

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

·    GObject 子类的 GTypeInfo 各个函数的作用

o  base_init:对 class 结构中继承过来的域进行设置。由于继承的域都是直接拷贝父类的 class 结构的,所以有些域可能需要进行深拷贝,这些操作就在这个函数完成。需要注意的是,假如 B 继承 A,是将 B class 结构指针传给 A 所定义的 base_init 函数,因为只有 A 自身知道如何恰当设置相关的域。

o  base_finalize:与 base_init 相反。

o  class_init:这个函数对每个类只会调用一次,class 结构对每个类来说也只有一个副本。这个函数有两个作用,一是对 class 结构中的域进行初始化,二是注册该类的属性、信号、私有数据。

o  class_finalize:与 class_init 相反。

o  instance_init:初始化实例对象。

·    例子代码及注释

static void my_ip_address_class_init (MyIPAddressClass *klass)

{

GObjectClass *gobject_class = G_OBJECT_CLASS (klass);  

gobject_class->set_property = my_ip_address_set_property;

gobject_class->get_property = my_ip_address_get_property;  

g_type_class_add_private (klass, sizeof (MyIPAddressPrivate));  

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);  

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);  

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);

}

 

转自:http://blog.sina.com.cn/s/blog_69c435f90100mh6z.html

posted on 2012-06-24 17:28  高原  阅读(702)  评论(0)    收藏  举报

导航