二、使用GObject——一个简单类的实现

Glib库实现了一个非常重要的基础类--GObject,这个类中封装了许多我们在定义和实现类时经常用到的机制:
  • 引用计数式的内存管理
  • 对象的构造与析构
  • 通用的属性(Property)机制
  • Signal的简单使用方式

很多使用GObject的库,如GTK+、GStreamer等,都是继承自GObject,所以如果没有特殊需要,我们使用GObject系统时,最好将GObject作为我们的单根父类。


下面我们就定义一个直接继承自GObject的类--TestDog,头文件test-dog.h的定义如下:

#ifndef TEST_DOG_H
#define TEST_DOG_H
#include <glib-object.h>
#define TEST_TYPE_DOG (test_dog_get_type())
#define TEST_DOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_DOG, TestDog))
#define TEST_IS_DOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TEST_TYPE_DOG))
#define TEST_DOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TEST_TYPE_DOG, TestDogClass))
#define TEST_IS_DOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TEST_TYPE_DOG))
#define TEST_DOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TEST_TYPE_DOG, TestDogClass))
typedef struct _TestDog TestDog;
typedef struct _TestDogClass TestDogClass;
struct _TestDog
{
GObject parent_instance;
char name[32];
int weight;
int height;
};
struct _TestDogClass
{
GObjectClass parent_class;
};
GType test_dog_get_type(void);
char* test_dog_get_name(TestDog* self);
void test_dog_set_name(TestDog* self, char* name);
int test_dog_get_weight(TestDog* self);
void test_dog_set_weight(TestDog* self, int weight);
int test_dog_get_height(TestDog* self);
void test_dog_set_height(TestDog* self, int height);
#endif



在声明宏、结构体、函数时,我们使用"test"或"Test"作为前缀,这是为了防止我们声明的内容,与他人声明的内容重名,有点类似于C++中namespace的概念。而GObject手册中也约定,在使用GObject系统定义类时,要在宏、结构体类型名、函数名前加入前缀。


GObject系统中的类包括两部分:类部分和对象部分,头文件中将这两部分分别定义为两个结构体。在系统运行过程中,对象部分结构体可以生成多个实例,而类部分结构体只能生成一个实例。类的成员变量一般定义在对象部分结构体中,而类部分结构体中一般定义静态成员变量、虚函数指针等。


test-dog.h中就定义了TestDog和TestDogClass两个结构体,TestDog是类的对象部分结构体,定义了name、weight、height三个成员变量,TestDogClass是类的类部分结构体。


为了方便类型检测和类型转换,我们定义了一些宏:
TEST_DOG(obj) :将obj指针强制转换为TestDog结构体类型
#define TEST_IS_DOG(obj):判断obj指针是否是TestDog结构体类型
#define TEST_DOG_CLASS(klass):将klass指针强制转换为TestDogClass结构体类型
#define TEST_IS_DOG_CLASS(klass):判断klass指针是否是TestDogClass结构体类型
#define TEST_DOG_GET_CLASS(obj):使用对象部分obj指针取得其类部分TestDogClass实例的指针


TEST_TYPE_DOG宏封装了对GType test_dog_get_type(void)函数的调用。由于GObject系统是在GType系统基础上建立的,所以我们创建的每一个类都需要注册到GType系统中,GType系统也会相应返回一个GType值,我们使用这个值创建类的对象。而GType test_dog_get_type(void)函数的功能就是将TestDog类注册到GType系统,并取得了相应的GType值。


由于TestDog类直接继承自GObject类,所以在定义类的对象部分和类部分结构体时,需要在结构体的起始位置分别加入了GObject类的对象部分和类部分结构体的实例。


TestDog类的公有成员函数很容易定义,直接在头文件中定义即可,只是需要将一个TestDog结构体类型(类的对象部分结构体)的指针作为参数,一般是函数的第一个参数。

源文件test-dog.c中实现了这个类,代码如下:

#include "
              test-dog.h
              "
              
#include <glib/gprintf.h>
#include <string.h>
static gpointer test_dog_class = NULL;
static void test_dog_class_init(gpointer g_class, gpointer g_class_data)
{
test_dog_class = g_class;
}
static void test_dog_instance_init(GTypeInstance *instance, gpointer g_class)
{
TestDog* dog_pointer = TEST_DOG(instance);
g_sprintf(dog_pointer->name, " NoName ");
dog_pointer->weight = 50; // 50kg
dog_pointer->height = 100; // 100cm
}
GType test_dog_get_type(void)
{
static GType type = 0;
if (type == 0) {
static const GTypeInfo info = {
sizeof (TestDogClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) test_dog_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (TestDog),
0, /* n_preallocs */
(GInstanceInitFunc) test_dog_instance_init /* instance_init */
};
type = g_type_register_static (G_TYPE_OBJECT,
" TestDogType ",
&info, 0);
}
return type;
}
char* test_dog_get_name(TestDog* self)
{
return self->name;
}
void test_dog_set_name(TestDog* self, char* name)
{
strncpy(self->name, name, 32);
}
int test_dog_get_weight(TestDog* self)
{
return self->weight;
}
void test_dog_set_weight(TestDog* self, int weight)
{
self->weight = weight;
}
int test_dog_get_height(TestDog* self)
{
return self->height;
}
void test_dog_set_height(TestDog* self, int height)
{
self->height = height;
}



GType test_dog_get_type(void)函数将TestDog类注册到GType系统中。函数中使用了GTypeInfo结构体,其声明如下:

struct _GTypeInfo
{
/* interface types, classed types, instantiated types */
guint16 class_size; // 类部分结构体的size

GBaseInitFunc base_init; // 与继承相关,创建该类的子类的对象时才会被调用
GBaseFinalizeFunc base_finalize; // 与继承相关,析构该类的子类的对象时才会被调用

/* interface types, classed types, instantiated types */
GClassInitFunc class_init; // 类部分结构体实例初始化函数
GClassFinalizeFunc class_finalize; // 类部分结构体实例的析构函数,该类的最后一个对象被删除时调用
gconstpointer class_data;

/* instantiated types */
guint16 instance_size; // 对象部分结构体的size
guint16 n_preallocs;
GInstanceInitFunc instance_init; // 对象部分结构体实例初始化函数

/* value handling */
const GTypeValueTable *value_table;
};



void test_dog_class_init(gpointer g_class, gpointer g_class_data)函数用来初始化类部分结构体实例,void test_dog_instance_init(GTypeInstance *instance, gpointer g_class)函数用来初始化对象部分结构体的实例。

创建第一个TestDog类对象时,会分别创建TestDog类的类部分结构体实例和对象部分结构体实例,并调用test_dog_class_init函数和test_dog_instance_init函数分别初始化,后续再创建TestDog类对象时,只会创建对象部分结构体实例,也只会调用test_dog_instance_init函数初始化。所以 test_dog_class_init函数只会在创建第一个TestDog类对象时被调用,而test_dog_instance_init函数在每次创建TestDog类对象时,都会被调用。


test_dog_class_init函数的第一个参数是TestDog类的类部分结构体实例的指针,函数中我们使用一个静态变量将其保存了下来。

到这里TestDog类就算定义完成了,测试一下吧。

#include <glib.h>
#include <glib/gstdio.h>
#include " test-dog.h "
int main(void)
{
TestDog* dog1 = NULL;
TestDog* dog2 = NULL;

g_type_init();
dog1 = (TestDog*)g_object_new(TEST_TYPE_DOG, NULL);
test_dog_set_name(dog1, " dog1 ");
g_printf(" dog1's name is %s/n ", test_dog_get_name(dog1));
g_printf(" dog1's weight is %d/n ", test_dog_get_weight(dog1));
g_printf(" dog1's height is %d/n ", test_dog_get_height(dog1));
dog2 = (TestDog*)g_object_new(TEST_TYPE_DOG, NULL);
test_dog_set_name(dog2, " dog2 ");
test_dog_set_weight(dog2, 30);
test_dog_set_height(dog2, 70);
g_printf(" dog2's name is %s/n ", test_dog_get_name(dog2));
g_printf(" dog2's weight is %d/n ", test_dog_get_weight(dog2));
g_printf(" dog2's height is %d/n ", test_dog_get_height(dog2));
g_object_unref(dog1);
g_object_unref(dog2);
}



转载于:https://my.oschina.net/u/1052241/blog/209628

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值