下面定义一个非抽象类MStrClass
。
首先使用类G_DECLARE_DERIVABLE_TYPE
来声明一个可继承类,和之前一样。
MStrClass
只有一个set_string()
类方法。这会被子类覆盖,比如MNumStr
。
这个方法会被公有函数m_str_set_string()
调用,set_string()
函数将设置实例的字符串。
m_str.h:
#ifndef M_STR_H
#define M_STR_H
#include <glib-object.h>
#define M_TYPE_STR (m_str_get_type())
G_DECLARE_DERIVABLE_TYPE (MStr, m_str, M, STR, GObject)
struct _MStrClass {
GObjectClass base_class;
void (*set_string) (MStr *self, const char *s);
};
MStr *
m_str_concat (MStr *self, MStr *other);
void
m_str_set_string (MStr *self, const char *s);
char *
m_str_get_string (MStr *self);
MStr *
m_str_new_with_string (const char *s);
MStr *
m_str_new (void);
#endif // M_STR_H
-
-enum定义属性ID,
str_properties
数组存储GParamSpec
的静态指针。 -
MStrPrivate
是一个结构体,是MStr
的private数据部分。
说下为什么要有MStrPrivate
:
如果MStr
不是final类型的,则不存在后代,而且可以在里面存储private数据。
但是,现在它是可继承的,如果把private数据放进去,那么就会被派生类访问到,所以不能放进MStr
中,而是放在另一个实结构,它的名称是<object name>Private
,这里就是MStrPrivate
。
这个private结构必须在G_DEFINE_TYPE_WITH_PRIVATE
之前定义。
G_DEFINE_TYPE_WITH_PRIVATE
扩展为:
m_str_class_init()
函数声明(类初始化函数)m_str_init()
函数声明(实例初始化函数)m_str_parent_class()
静态变量的定义,它指向MStr
的父类。- 将private数据实例添加到类型中,这里private数据是
MStrPrivate
。 m_str_get_type()
函数定义,该函数在第一次调用时注册类型。m_str_get_instance_private()
函数定义,用来获取private实例。函数的参数是指向实例的指针。
m_str_set_property()
和以前一样,只不过这里属性值放在private区域,它通过调用m_str_set_string()
来设置private数据区。这很重要,因为m_str_set_string()
会调用set_string()
,而set_string()
又会被子类覆盖。
因此,m_str_set_property()
会设置子类的字符串。g_value_get_string()
返回字符串指针,你不能拥有该字符串。这意味着亦不能更改、释放它。因此需要在存储之前复制,复制已经在m_str_set_string()
函数内部完成了。
m_str_get_property()
调用m_str_get_strnig()
,复制在函数内部完成。
m_str_real_set_string()
是类方法set_string()
的主体。首先,它调用m_str_get_instance_private()
来获取指向private区域的指针;如果实例中有字符串,就释放它,然后new一个新的;它拷贝字符串并且放入priv->string
。
m_str_finalize()
在MStr
实例被销毁时调用,它释放priv->string
;然后,它调用父类的finalize
方法。
m_str_init()
初始化riv->string
。
m_str_class_init()
初始化MStr
类;
- 这个函数中覆盖了
finalize
方法,在销毁过程中调用; - 然后覆盖
set_property()
、get_property()
,创建GParamSpec
并安装属性; set_string
方法指向m_str_real_set_string
,这个方法可以在子类中被替换。
setter和getter被m_str_set_property
和m_str_get_property
调用。setter方法m_str_set_string()
调用的是类方法set_string()
,所以它的行为会在子类中改变。
m_str_concat``连接字符串来创建一个新的
MStr`。
m_str.c:
#include "m_str.h"
enum {
PROP_0,
PROP_STRING,
N_PROPERTIES
};
static GParamSpec *str_properties[N_PROPERTIES] = { NULL};
typedef struct {
char *string;
} MStrPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (MStr, m_str, G_TYPE_OBJECT)
static void
m_str_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
MStr *self = M_STR (object);
if (property_id == PROP_STRING)
m_str_set_string (self, g_value_get_string (value));
else
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
m_str_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
MStr *self = M_STR (object);
if (property_id == PROP_STRING)
g_value_set_string (value, m_str_get_string (self));
else
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
m_str_real_set_string (MStr *self, const char *s)
{
MStrPrivate *priv = m_str_get_instance_private (self);
if (priv->string)
g_free (priv->string);
priv->string = g_strdup (s);
}
static void
m_str_finalize (GObject *object)
{
MStr *self = M_STR (object);
MStrPrivate *priv = m_str_get_instance_private (self);
if (priv->string)
g_free (priv->string);
G_OBJECT_CLASS (m_str_parent_class)->finalize (object);
}
static void
m_str_init (MStr *self)
{
MStrPrivate *priv = m_str_get_instance_private (self);
priv->string = NULL;
}
static void
m_str_class_init (MStrClass *class_)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class_);
gobject_class->finalize = m_str_finalize;
gobject_class->set_property = m_str_set_property;
gobject_class->get_property = m_str_get_property;
str_properties[PROP_STRING] = g_param_spec_string (
"string", "str", "string",
"", G_PARAM_READWRITE
);
class_->set_string = m_str_real_set_string;
}
void
m_str_set_string (MStr *self, const char *s)
{
g_return_if_fail (M_IS_STR (self));
MStrClass *class_ = M_STR_GET_CLASS (self);
class_->set_string (self, s);
}
char *
m_str_get_string (MStr *self)
{
g_return_val_if_fail (M_IS_STR (self), NULL);
MStrPrivate *priv = m_str_get_instance_private (self);
return g_strdup (priv->string);
}
MStr *
m_str_concat (MStr *self, MStr *other)
{
g_return_val_if_fail (M_IS_STR (self), NULL);
g_return_val_if_fail (M_IS_STR (other), NULL);
char *s1, *s2, *s3;
MStr *str;
s1 = m_str_get_string (self);
s2 = m_str_get_string (other);
if (s1 && s2)
s3 = g_strconcat (self);
else if (s1)
s3 = g_strdup (s1);
else if (s2)
s3 = g_strdup (s2);
else
s3 = NULL;
str = m_str_new_with_string (s3);
if (s1)
g_free (s1);
if (s2)
g_free (s2);
if (s3)
g_free (s3);
return str;
}
MStr *
m_str_new_with_string (const char *s)
{
return M_STR (g_object_new (M_TYPE_STR, "string", s, NULL));
}
MStr *
m_str_new (void)
{
return M_STR (g_object_new (M_TYPE_STR, NULL));
}
编写可派生类的总结:
- 在头文件中使用
G_DECLARE_DERIVABLE_TYPE
宏, 在它之前需要定义宏#define M_TYPE_STR (m_str_get_type())
- 在头文件中定义类结构体,它的内容对子类可见,大多数成员是类方法。
- 在.c文件中使用
G_DEFINE_TYPE_WITH_PRIVATE
,在它之前需要定义private数据区,它是一个名为<object name>Private
的结构体,如MStrPrivate
。 - 定义类和实例初始化函数。