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
  1. -enum定义属性ID,str_properties数组存储GParamSpec的静态指针。

  2. MStrPrivate是一个结构体,是MStr的private数据部分。

说下为什么要有MStrPrivate
如果MStr不是final类型的,则不存在后代,而且可以在里面存储private数据。

但是,现在它是可继承的,如果把private数据放进去,那么就会被派生类访问到,所以不能放进MStr中,而是放在另一个实结构,它的名称是<object name>Private,这里就是MStrPrivate

这个private结构必须在G_DEFINE_TYPE_WITH_PRIVATE之前定义。

G_DEFINE_TYPE_WITH_PRIVATE扩展为:

  1. m_str_class_init()函数声明(类初始化函数)
  2. m_str_init()函数声明(实例初始化函数)
  3. m_str_parent_class()静态变量的定义,它指向MStr的父类。
  4. 将private数据实例添加到类型中,这里private数据是MStrPrivate
  5. m_str_get_type()函数定义,该函数在第一次调用时注册类型。
  6. 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类;

  1. 这个函数中覆盖了finalize方法,在销毁过程中调用;
  2. 然后覆盖set_property()get_property(),创建GParamSpec并安装属性;
  3. set_string方法指向m_str_real_set_string,这个方法可以在子类中被替换。

setter和getter被m_str_set_propertym_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));
}

编写可派生类的总结:

  1. 在头文件中使用G_DECLARE_DERIVABLE_TYPE宏, 在它之前需要定义宏#define M_TYPE_STR (m_str_get_type())
  2. 在头文件中定义类结构体,它的内容对子类可见,大多数成员是类方法。
  3. 在.c文件中使用G_DEFINE_TYPE_WITH_PRIVATE,在它之前需要定义private数据区,它是一个名为<object name>Private的结构体,如MStrPrivate
  4. 定义类和实例初始化函数。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

barbyQAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值