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

  定义 GObject 的子类

    * instance 结构:包含于类的实例相关的域,相当于 C++ 中的非静态公共成员。
    * class 结构:包含的域相当于 C++ 中的静态公共成员。
    * 私有成员在哪里定义?

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

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

/* myipaddress.h */
 
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtkentry.h>
 
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 类的实现以及对象属性中还会进一步介绍这些函数被调用的顺序。

    * 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);
 
  /* 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);
}

 原文地址 http://www.thoss.org.cn/mediawiki/index.php/%E5%AE%9A%E4%B9%89%E3%80%81%E6%B3%A8%E5%86%8C%E5%92%8C%E

你可能感兴趣的:(数据结构,C++,c,PHP,C#)