4. 派生类和非抽象类
下面定义一个非抽象类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));elseG_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));elseG_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);elses3 = 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
。 - 定义类和实例初始化函数。