本文版权所有,转载请注明出处和作者联系方式。
作者:孙华明
联系方式: wormsun at gmail.com
上篇中实现的lw_new和lw_delete函数只能创建和删除animal类的对象,这当然满足不了我们的需要,那么如何实现通用的lw_new和lw_delete函数,用来创建和删除任何类的对象呢?
考虑新定义一个类car,其属性有车标logo和颜色color:
typedef struct _car car;
struct _car
{
/*private data*/
char logo[256];
int color;
};
成员函数:
char* car_get_logo(car* self);
void car_set_logo(car* self, char* logo);
int car_get_color(car* self);
void car_set_color(car* self, int color);
使用lw_new创建car类对象时,必须知道car类的对象占用内存空间大小和构造函数,以便为对象申请内存空间,并初始化该对象,而使用lw_delete函数删除car类的对象时,就必须知道car类的析构函数,以便先析构该对象,然后再释放对象占用的内存空间。可以看出,创建和删除car类的对象所需要的三个必备条件就是:类的对象占用内存空间大小,构造函数和析构函数,与创建和删除animal类时所需的条件一致。那么我们是不是可以创建一个统一的类信息结构体,然后使用结构体的不同对象,保存不同类的类信息呢?
上篇中定义的animal类的类信息结构体如下:
struct _animal_klass_info
{
animal_klass_info* super; /*animal's super's klass info*/
char* name; /*animal klass's name*/
size_t size; /*animal's size*/
animal* (*ctor)(animal* self); /*constructor*/
animal* (*dtor)(animal* self); /*destructor*/
};
super为父类的类信息地址,无父类,则其值为NULL,name是该类的名称,这两个成员先不考虑,我们着重考虑size、ctor和dtor这三个成员,看看是否能够将这个animal专用的类信息结构体,改造成统一的类信息结构体。size为类对象所占内存空间大小,类型为size_t,这对任何类都是通用的,所以无需变更。ctor和dtor都是函数指针类型,传入参数为animal对象的地址,在将animal对象初始化或析构后,再将该地址返回。既然要适用于任意类,在类信息结构体中就不能使用animal类的指针了,我们将结构体中这两个函数的参数和返回值类型弱化为void*类型,如下:
typedef void* (*voidf)(void*);
voidf ctor;
voidf dtor;
注意修改的只是类信息结构体中的函数类型,在实现animal类的构造函数和析构函数时,仍然可以直接使用animal类的指针类型,这样在初始化类信息时,编译器会警告类型不兼容,为了避免警告,我们将类信息结构体中的构造函数和析构函数指针弱化为void*类型,最终类信息结构体的定义为:
typedef struct _klass_info klass_info;
struct _klass_info
{
klass_info* super; /*object's klass's super klass*/
char* name; /*object's klass's name*/
size_t size; /*object's size*/
void* ctor; /*object's constructor*/
void* dtor; /*object's destructor*/
};
lw_new和lw_delete函数的实现也要响应修改,如下:
void* lw_new(void* klass)
{
klass_info* kls = KLASS(klass);
void* p = malloc(kls->size);
*((klass_info**)p) = kls;
return ((voidf)(kls->ctor))(p);
}
void lw_delete(void* self)
{
if(self)
{
klass_info* kls = *((klass_info**)self);
free(((voidf)(kls->dtor))(self));
}
}
为了避免警告,lw_new和lw_delete函数的传入参数类型也弱化为void*类型。klass_info* kls = KLASS(klass)中使用了KLASS()宏,定义如下:
#define KLASS(_klass_info_) ((klass_info*)_klass_info_)
用来将其他指针强制转换为klass_info*类型。
我们以car类为例,看看如何使用klass_info类信息结构体定义类。
car.h
extern klass_info* car_klass;
#define CAR(_object_) ((car*)_object_)
typedef struct _car car;
struct _car
{
/*class info*/
klass_info* klass;
/*private data*/
char logo[256];
int color;
};
char* car_get_logo(car* self);
void car_set_logo(car* self, char* logo);
int car_get_color(car* self);
void car_set_color(car* self, int color);
car.c
#include "car.h"
static car* car_ctor(car* self);
static car* car_dtor(car* self);
static klass_info local_car_klass =
{
NULL,
"car_klass",
sizeof(car),
car_ctor,
car_dtor,
};
klass_info* car_klass = &local_car_klass;
static car* car_ctor(car* self)
{
memset(self->logo, 0x00, sizeof(self->logo));
self->color = 0;
}
static car* car_dtor(car* self)
{
return self;
}
char* car_get_logo(car* self)
{
return self->logo;
}
void car_set_logo(car* self, char* logo)
{
strncpy(self->logo, logo, sizeof(self->logo));
}
int car_get_color(car* self)
{
return self->color;
}
void car_set_color(car* self, int color)
{
self->color = color;
}
下面测试一下:
int main(int argc, char* argv)
{
argc;
argv;
animal* animal1 = NULL;
animal* animal2 = NULL;
car* car1 = NULL;
car* car2 = NULL;
animal1 = lw_new(animal_klass);
animal_set_name(animal1, "Kitty");
animal_set_weight(animal1, 30);
printf("animal1, name : %s, weight : %d/n",
animal_get_name(animal1),
animal_get_weight(animal1));
lw_delete(animal1);
animal2 = lw_new(animal_klass);
animal_set_name(animal2, "Bib");
animal_set_weight(animal2, 10);
printf("animal2, name : %s, weight : %d/n",
animal_get_name(animal2),
animal_get_weight(animal2));
lw_delete(animal2);
car1 = lw_new(car_klass);
car_set_logo(car1, "Honda");
car_set_color(car1, 0);
printf("car1, logo : %s, color : %d/n",
car_get_logo(car1),
car_get_color(car1));
lw_delete(car1);
car2 = lw_new(car_klass);
car_set_logo(car2, "BMW");
car_set_color(car2, 1);
printf("car2, logo : %s, color : %d/n",
car_get_logo(car2),
car_get_color(car2));
lw_delete(car2);
return 0;
}
到这里我们已经能够使用lw_new和lw_delete函数创建和删除任何类的对象,并且自动调用其构造函数和析构函数了,本篇文章相关代码可以到这里下载。
下篇文章我们将讨论继承。