GObject参考手册(8)--基类:GObject

本文引用自http://imtx.cn,原文作者:TualatriX 

原文:http://library.gnome.org/devel/gobject/2.14/chapter-gobject.html

前面两个章节讨论了Glib动态类型系统的细节和它的信号控制系统。GObject库同时也包括了一个最基本的类型即基类,即为GObject。

GObject是一个类化的可实例的基类。它实现了:

  • 使用引用计数的内存管理。
  • 实例的构造和析构 。
  • 使用set/get函数对进行的一般性属性操作。
  • 简易使用的信号机制。

所有使用GLib类型系统的GNOME库(如GTK+和GStreamer)都由GObject继承,所以了解有关它是如何工作的细节是如此重要。

对象实例

g_object_new系列函数可以被用作实例化由GObject基本类型继承的一些GType。所有的这些函数确保类和实例的结构被Glib的类型系统正确的初始化,然后调用一个或别的构造类方法用于:

  • 通过g_type_create_instance来分配和清理内存。
  • 初始化对象的实例用构造属性。

尽管你可以期待所有的类和实例成员(指向父类的部分)都会设置为0,但是尽量考虑自己明确地去设置它们。
由GObject继承的对象们允许重载它的类构造方法:在做下面之前它们需要连接到父类的构造方法:

  GObject*   (*constructor)     (GType                  type,
                                 guint                  n_construct_properties,
                                 GObjectConstructParam *construct_properties);

下面的代码显示MamanBar重载了父类的构造器:

#define MAMAN_TYPE_BAR                  (maman_bar_get_type ())
#define MAMAN_BAR(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar))
#define MAMAN_BAR_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass))
#define MAMAN_IS_BAR(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR))
#define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR))
#define MAMAN_BAR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass))

typedef struct _MamanBar MamanBar;
typedef struct _MamanBarClass MamanBarClass;

struct _MamanBar {
  GObject parent;
  /* instance members */
};

struct _MamanBarClass {
  GObjectClass parent;

  /* class members */
};

/* used by MAMAN_TYPE_BAR */
GType maman_bar_get_type (void);

static GObject *
maman_bar_constructor (GType                  type,
                       guint                  n_construct_properties,
                       GObjectConstructParam *construct_properties)
{
  GObject *obj;

  {
    /* Invoke parent constructor. */
    MamanBarClass *klass;
    GObjectClass *parent_class;
    klass = MAMAN_BAR_CLASS (g_type_class_peek (MAMAN_TYPE_BAR));
    parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
    obj = parent_class->constructor (type,
                                     n_construct_properties,
                                     construct_properties);
  }

  /* do stuff. */

  return obj;
}

static void
maman_bar_instance_init (GTypeInstance   *instance,
                         gpointer         g_class)
{
  MamanBar *self = (MamanBar *)instance;
  /* do stuff */
}

static void
maman_bar_class_init (gpointer g_class,
                      gpointer g_class_data)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
  MamanBarClass *klass = MAMAN_BAR_CLASS (g_class);

  gobject_class->constructor = maman_bar_constructor;
}

GType maman_bar_get_type (void)
{
  static GType type = 0;
  if (type == 0) {
    static const GTypeInfo info = {
      sizeof (MamanBarClass),
      NULL,   /* base_init */
      NULL,   /* base_finalize */
      maman_bar_class_init,   /* class_init */
      NULL,   /* class_finalize */
      NULL,   /* class_data */
      sizeof (MamanBar),
      0,      /* n_preallocs */
      maman_bar_instance_init    /* instance_init */
    };
    type = g_type_register_static (G_TYPE_OBJECT,
                                   "MamanBarType",
                                   &info, 0);
  }
  return type;
}

如果用户是用下面的方法创建一个MamanBar的实例的:

MamanBar *bar = g_object_new (MAMAN_TYPE_BAR, NULL);

如果这是用户创建的第一个实例,那么maman_b_class_init函数将会在maman_b_base_class_init 调用后才调用。这将确保这个新对象的类结构会正确的初始化。这里,mana_bar_class_init被期望重载对象的类方法,并且设置这个类的自己的方法。在上面的例子中,构造方法是唯一被重载的方法:它设置为了maman_bar_constructor。

一旦g_object_new已经得到了一个初始化后的类结构的引用,它就调用它的构造方法来为这个新对象创建一个实例。因为它刚刚被maman_bar_class_init由maman_bar_constructor重载,后者将被调用,因为它是实现正确的,它也连接了父类的构造器。问题是,我们怎么才能找到父类的构造器。一个接近(在GTK+源代码中使用)将会来保存原始的构造器在一个从maman_class_init来的静态变量中,然后可以通过maman_bar_constructor来重用它。这是足够清楚的,也非常简单,但是我说这不是很好的办法,更好的办法是用g_type_class_peek和g_type_class_peek_parent函数。

最终,在长链中一个g_object_constructor被最后一个构造器调用。这个函数通过g_type_create_instance分配的对象实例的缓存,这意味着如果它被注册过的话instance_init函数将在这点上被调用。在instance_init返回以后,对象就完全初始化好了,应该准备好应答用户的请求了。当g_type_create_instance返回了,g_object_constructor设置构造属性(属性由g_object_new给的)并返回用户的构造器,使它来允许完成一些有用的实例初始化。

这个所描述的处理过程看起来有一点难懂(在我看来确实难懂),但是由下面的表格可以清楚的概况起来,当g_object_new调用时,相关函数的调用顺序。

箭头表示由g_object_new进行的函数调用和它们的调用顺序。
表格太大,不贴了
读者可能会对函数调用的顺序感到不安:当然,技术上来说, 类结构方法总是在GType的instance_init调用之前被调用(因为g_type_create_instance来调用instance_init相当于由g_object_constructor来调用顶级类的构造器,这是用户所期望的),运行着用户提供的构造器的用户的代码将总是在GType的instance_init函数之后运行,因为用户提供的构造器必须在做一些有用的事情前被链接好。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值