Instantiatable classed types Initialization and destruction
类型的实例化是通过函数g_tpye_create_instance()实现的。这个函数首先会查找与类型相关的GTypeInfo结构体,之后,查询结构体中的instance_size和 instance policy即 n_preallocs(在 2.10版本后废弃)。
如果正在创建的实例时此对象的第一个实例,类型系统必须为对象类结构体分配空间并进行初始化。在初始化时,类结构体的第一部分数据是通过复制此类的父类的结构体。类结构体的其余数据赋值为0.如果对象的类无父类,则对象的类结构体初始化为0.之后,类型系统从最顶端的基础对象开始,根据继承关系,直到现在正在实例化的对象为止,顺序调用base_init,接着,调用对象的class_init来完成类结构的初始化,最后初始化接口。
一旦类型系统初始化完成一个类结构,它就会将实例化的对象的类指针指向这个完成初始化的类结构。这就实现了多个实例化对象共用一个初始化类结构体。之后,会调用对象的instance_init函数,调用顺序是从最顶端的基础对象开始,根据继承关系,直到正在实例化的对象。
对象实例化的销毁,是通过函数g_type_free_instance()实现的。它的功能很简单:它会将实例结构体返还到实例池(这是池是根据n_preallocs分配的)。如果这个实例是一个对象的最后一个实例,那么,当实例销毁时,此对象的类初始化结构体也会被销毁。
类的销毁过程与类结构体初始化过程是对称的。即,先销毁接口,接着调用对象的class_finalize,最后,按照与调用class_init相反的顺序调用base_finalize。
过程如下图所示:
Non-instantiatable classed types: interfaces
interface与抽象类相似,只不过,interface中定义的通用API能被没有继承关系的类使用。它定义了一组方法,在Glib中,我们称这组方法为虚函数,这些方法是由实现接口的类来实现的。不同的类可以有相同的接口方法集。比如,CD播放器、MP3等设备上都有播放、暂停和停止按钮。此时,我们可以认为播放、暂停和停止属于接口中的方法集。
那么,接口是怎么定义以及使用的呢?下面我们举例说明,值得注意的是,接口的定义只需要一个数据结构,而不像对象,需要定义实例和类这两个数据结构,且我们定义的接口的数据结构第一个成员必须为GTypeInterface。GTypeInterface是所有接口的基类。其他成员还有接口函数指针。
我们实现的接口名为TComparable, 接口中方法为cmp。
/*tcomparable.h*/
#pragma once
#include <glib-object.h>
#define T_TYPE_COMPARABLE (t_comparable_get_type ())
/*G_DECLARE_INTERFACE (TComparable, t_comparable, T, COMPARABLE, GObject)*/
GType t_comparable_get_type (void);
typedef struct _TComparable TComparable;
typedef struct _TComparableInterface TComparableInterface;
#define T_COMPARABLE(instance) (G_TYPE_CHECK_INSTANCE_CAST(instance, T_TYPE_COMPARABLE, TComparable))
#define T_IS_COMPARABLE(instance) (G_TYPE_CHECK_INSTANCE_TYPE(instance, T_TYPE_COMPARABLE))
#define T_COMPARABLE_GET_IFACE(instance) (G_TYPE_INSTANCE_GET_INTERFACE((instance), T_TYPE_COMPARABLE, TComparableInterface))
struct _TComparableInterface {
GTypeInterface parent;
/* signal */
void (*arg_error) (TComparable *self);
/* virtual function 将由实现接口的类的方法来覆盖*/
int (*cmp) (TComparable *self, TComparable *other);
};
/* t_comparable_cmp */
/* if self > other, then returns 1 */
/* if self = other, then returns 0 */
/* if self < other, then returns -1 */
/* if error happens, then returns -2 */
int
t_comparable_cmp (TComparable *self, TComparable *other);
gboolean
t_comparable_eq (TComparable *self, TComparable *other);
gboolean
t_comparable_gt (TComparable *self, TComparable *other);
gboolean
t_comparable_lt (TComparable *self, TComparable *other);
gboolean
t_comparable_ge (TComparable *self, TComparable *other);
gboolean
t_comparable_le (TComparable *self, TComparable *other);
/*tcomparable.c*/
#include "tcomparable.h"
static guint t_comparable_signal;
static void
t_comparable_default_init (TComparableInterface *iface);
GType
t_comparable_get_type (void) {
static GType type = 0;
GTypeInfo info;
if (type == 0) {
info.class_size = sizeof (TComparableInterface);
info.base_init = NULL;
info.base_finalize = NULL;
info.class_init = (GClassInitFunc) t_comparable_default_init;
info.class_finalize = NULL;
info.class_data = NULL;
info.instance_size = 0;
info.n_preallocs = 0;
info.instance_init = NULL;
info.value_table = NULL;
type = g_type_register_static (G_TYPE_INTERFACE, "TComparable", &info, 0);
}
return type;
}
static void
arg_error_default_cb (TComparable *self) {
g_printerr ("\nTComparable: argument error.\n");
}
static void
t_comparable_default_init (TComparableInterface *iface) {
/* virtual function */
iface->cmp = NULL;
/* argument error signal */
iface->arg_error = arg_error_default_cb;
t_comparable_signal =
g_signal_new ("arg-error",
T_TYPE_COMPARABLE,
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
G_STRUCT_OFFSET (TComparableInterface, arg_error),
NULL /* accumulator */,
NULL /* accumulator data */,
NULL /* C marshaller */,
G_TYPE_NONE /* return_type */,
0 /* n_params */,
NULL /* param_types */);
}
int
t_comparable_cmp (TComparable *self, TComparable *other) {
g_return_val_if_fail (T_IS_COMPARABLE (self), -2);
TComparableInterface *iface = T_COMPARABLE_GET_IFACE (self);
return (iface->cmp == NULL ? -2 : iface->cmp (self, other));
}
gboolean
t_comparable_eq (TComparable *self, TComparable *other) {
return (t_comparable_cmp (self, other) == 0);
}
gboolean
t_comparable_gt (TComparable *self, TComparable *other) {
return (t_comparable_cmp (self, other) == 1);
}
gboolean
t_comparable_lt (TComparable *self, TComparable *other) {
return (t_comparable_cmp (self, other) == -1);
}
gboolean
t_comparable_ge (TComparable *self, TComparable *other) {
int result = t_comparable_cmp (self, other);
return (result == 1 || result == 0);
}
gboolean
t_comparable_le (TComparable *self, TComparable *other) {
int result = t_comparable_cmp (self, other);
return (result == -1 || result == 0);
}
到此,我们完成了对接口TComparable的定义。下面,我们来说明如何在各个类中使用,以及接口的虚拟函数是如何被各个类的函数覆盖的。
/*tint.c*/
#include "../tnumber/tnumber.h"
#include "../tnumber/tint.h"
#include "../tnumber/tdouble.h"
#include "tcomparable_without_macro.h"
enum {
PROP_0,
PROP_INT,
N_PROPERTIES
};
static GParamSpec *int_properties[N_PROPERTIES] = {NULL, };
struct _TInt {
TNumber parent;
int value;
};
static void t_comparable_interface_init (TComparableInterface *iface);
static void
t_int_class_init (TIntClass *class);
static void
t_int_init (TInt *self);
GType
t_int_get_type (void)
{
static GType type = 0;
if (type == 0) {
const GTypeInfo info = {
sizeof (TIntClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) t_int_class_init, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (TInt),
0, /* n_preallocs */
(GInstanceInitFunc) t_int_init, /* instance_init */
NULL /* value table */
};
const GInterfaceInfo comparable_info = {
(GInterfaceInitFunc) t_comparable_interface_init, /* interface_init */
NULL, /* interface_finalize */
NULL /* interface_data */
};
type = g_type_register_static (T_TYPE_NUMBER, "TInt", &info, 0);
g_type_add_interface_static (type, T_TYPE_COMPARABLE, &comparable_info);
}
return type;
}
static int
t_int_comparable_cmp (TComparable *self, TComparable *other) {
if (! T_IS_NUMBER (other)) {
g_signal_emit_by_name (self, "arg-error");
return -2;
}
int i;
double s, o;
s = (double) T_INT (self)->value;
if (T_IS_INT (other)) {
g_object_get (other, "value", &i, NULL);
o = (double) i;
} else
g_object_get (other, "value", &o, NULL);
if (s > o)
return 1;
else if (s == o)
return 0;
else if (s < o)
return -1;
else /* This can't happen. */
return -2;
}
static void
t_comparable_interface_init (TComparableInterface *iface) {
iface->cmp = t_int_comparable_cmp;
}
static void
t_int_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) {
TInt *self = T_INT (object);
if (property_id == PROP_INT)
self->value = g_value_get_int (value);
else
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
t_int_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) {
TInt *self = T_INT (object);
if (property_id == PROP_INT)
g_value_set_int (value, self->value);
else
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
t_int_init (TInt *self) {
}
/* arithmetic operator */
/* These operators create a new instance and return a pointer to it. */
#define t_int_binary_op(op) \
int i; \
double d; \
if (T_IS_INT (other)) { \
g_object_get (T_INT (other), "value", &i, NULL); \
return T_NUMBER (t_int_new_with_value (T_INT(self)->value op i)); \
} else { \
g_object_get (T_DOUBLE (other), "value", &d, NULL); \
return T_NUMBER (t_int_new_with_value (T_INT(self)->value op (int) d)); \
}
static TNumber *
t_int_add (TNumber *self, TNumber *other) {
g_return_val_if_fail (T_IS_INT (self), NULL);
t_int_binary_op (+)
}
static TNumber *
t_int_sub (TNumber *self, TNumber *other) {
g_return_val_if_fail (T_IS_INT (self), NULL);
t_int_binary_op (-)
}
static TNumber *
t_int_mul (TNumber *self, TNumber *other) {
g_return_val_if_fail (T_IS_INT (self), NULL);
t_int_binary_op (*)
}
static TNumber *
t_int_div (TNumber *self, TNumber *other) {
g_return_val_if_fail (T_IS_INT (self), NULL);
int i;
double d;
if (T_IS_INT (other)) {
g_object_get (T_INT (other), "value", &i, NULL);
if (i == 0) {
g_signal_emit_by_name (self, "div-by-zero");
return NULL;
} else
return T_NUMBER (t_int_new_with_value (T_INT(self)->value / i));
} else {
g_object_get (T_DOUBLE (other), "value", &d, NULL);
if (d == 0) {
g_signal_emit_by_name (self, "div-by-zero");
return NULL;
} else
return T_NUMBER (t_int_new_with_value (T_INT(self)->value / (int) d));
}
}
static TNumber *
t_int_uminus (TNumber *self) {
g_return_val_if_fail (T_IS_INT (self), NULL);
return T_NUMBER (t_int_new_with_value (- T_INT(self)->value));
}
static char *
t_int_to_s (TNumber *self) {
g_return_val_if_fail (T_IS_INT (self), NULL);
int i;
g_object_get (T_INT (self), "value", &i, NULL);
return g_strdup_printf ("%d", i);
}
static void
t_int_class_init (TIntClass *class) {
TNumberClass *tnumber_class = T_NUMBER_CLASS (class);
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
/* override virtual functions */
tnumber_class->add = t_int_add;
tnumber_class->sub = t_int_sub;
tnumber_class->mul = t_int_mul;
tnumber_class->div = t_int_div;
tnumber_class->uminus = t_int_uminus;
tnumber_class->to_s = t_int_to_s;
gobject_class->set_property = t_int_set_property;
gobject_class->get_property = t_int_get_property;
int_properties[PROP_INT] = g_param_spec_int ("value", "val", "Integer value", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE);
g_object_class_install_properties (gobject_class, N_PROPERTIES, int_properties);
}
TInt *
t_int_new_with_value (int value) {
TInt *i;
i = g_object_new (T_TYPE_INT, "value", value, NULL);
return i;
}
TInt *
t_int_new (void) {
TInt *i;
i = g_object_new (T_TYPE_INT, NULL);
return i;
}
tstr.c、tdouble.c与tint.c使用Tcompararble的方式相同。到此,数据类型都定义完成,接下来说明,在应用程序中如何使用TComparable。
#include <glib-object.h>
#include "tcomparable_without_macro.h"
#include "../tnumber/tnumber.h"
#include "../tnumber/tint.h"
#include "../tnumber/tdouble.h"
#include "../tstr/tstr.h"
static void
t_print (const char *cmp, TComparable *c1, TComparable *c2) {
char *s1, *s2;
TStr *ts1, *ts2, *ts3;
ts1 = t_str_new_with_string("\"");
if (T_IS_NUMBER (c1))
s1 = t_number_to_s (T_NUMBER (c1));
else if (T_IS_STR (c1)) {
ts2 = t_str_concat (ts1, T_STR (c1));
ts3 = t_str_concat (ts2, ts1);
s1 = t_str_get_string (T_STR (ts3));
g_object_unref (ts2);
g_object_unref (ts3);
} else {
g_print ("c1 isn't TInt, TDouble nor TStr.\n");
return;
}
if (T_IS_NUMBER (c2))
s2 = t_number_to_s (T_NUMBER (c2));
else if (T_IS_STR (c2)) {
ts2 = t_str_concat (ts1, T_STR (c2));
ts3 = t_str_concat (ts2, ts1);
s2 = t_str_get_string (T_STR (ts3));
g_object_unref (ts2);
g_object_unref (ts3);
} else {
g_print ("c2 isn't TInt, TDouble nor TStr.\n");
return;
}
g_print ("%s %s %s.\n", s1, cmp, s2);
g_object_unref (ts1);
g_free (s1);
g_free (s2);
}
static void
compare (TComparable *c1, TComparable *c2) {
if (t_comparable_eq (c1, c2))
t_print ("equals", c1, c2);
else if (t_comparable_gt (c1, c2))
t_print ("is greater than", c1, c2);
else if (t_comparable_lt (c1, c2))
t_print ("is less than", c1, c2);
else if (t_comparable_ge (c1, c2))
t_print ("is greater than or equal to", c1, c2);
else if (t_comparable_le (c1, c2))
t_print ("is less than or equal to", c1, c2);
else
t_print ("can't compare to", c1, c2);
}
int
main (int argc, char **argv) {
const char *one = "one";
const char *two = "two";
const char *three = "three";
TInt *i;
TDouble *d;
TStr *str1, *str2, *str3;
i = t_int_new_with_value (124);
d = t_double_new_with_value (123.45);
str1 = t_str_new_with_string (one);
str2 = t_str_new_with_string (two);
str3 = t_str_new_with_string (three);
compare (T_COMPARABLE (i), T_COMPARABLE (d));
compare (T_COMPARABLE (str1), T_COMPARABLE (str2));
compare (T_COMPARABLE (str2), T_COMPARABLE (str3));
compare (T_COMPARABLE (i), T_COMPARABLE (str1));
g_object_unref (i);
g_object_unref (d);
g_object_unref (str1);
g_object_unref (str2);
g_object_unref (str3);
return 0;
}
Interface initialization
与abstract class类似,初始化时,会为接口结构分配空间,之后将父接口结构复制进来,如果没有父接口结构,则新分配的结构会被初始化为0。之后将g_type设置为正在初始化的接口类型,g_instance_type 设置为此正在实现此接口的对象类型。接着调用接口结构中base_init、default_init(class_int)、接口实现时所在类型的interface_init(例如Tint中的 t_compare_interface_init).如果同一个接口在很多类型中都存在实现(例如,Tcomparable在Tint,Tstr中都有实现),那么,接口实现所在的每个类型在初始化过程中,当轮到接口初始化时,接口中的base_init、interface_init都要执行一次,然而,default_init却只执行一次。因此,对接口的初始化一般放在default_init中,就如上例中的t_comparable_default_init中设置信号那样。
过程及说明如下表所示:
Interface Destruction
当接口实现所在类型的最后一个实例化(如TInt的实例化)销毁时,与此类型相关的接口的实现也会销毁。
在销毁接口的实现时,类型系统GType会首先调用接口实现的interface_finalize之后调用接口的base_finalize。同样的每个接口实现所在类型在销毁时,这两个函数都被被调用一次。当所有接口实现所在类的实例都被销毁时,接口类型才会被销毁。
过程及说明如下表所示: