- 引用计数式的内存管理
- 对象的构造与析构
- 通用的属性(Property)机制
- Signal的简单使用方式
很多使用GObject的库,如GTK+、GStreamer等,都是继承自GObject,所以如果没有特殊需要,我们使用GObject系统时,最好将GObject作为我们的单根父类。
下面我们就定义一个直接继承自GObject的类--TestDog,头文件test-dog.h的定义如下:
#ifndef TEST_DOG_H
#define TEST_DOG_H
#include 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
#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
#include
#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);
}