C语言也能面向对象(四)——继承
2011-05-08 03:57 wormsun 阅读(381) 评论(0) 收藏 举报本文版权所有,转载请注明出处和作者联系方式。
作者:孙华明
联系方式: wormsun at gmail.com
在C++中如果一个类有父类,那么这个类的对象中就包含了父类中定义的数据,并且可以使用父类的函数访问或操作该这些数据。在C中如何实现这样的机制呢?
animal类的定义如下:
typedef struct _animal animal;
struct _animal
{
/*class info*/
klass_info* klass;
/*private data*/
char name[256];
int weight;
};
现在我们再定义一个dog类,除了包含animal类的属性外,还包括一个age属性,即年龄。如下:
typedef struct _dog dog ;
struct _dog
{
/*class info*/
klass_info* klass;
/*private data*/
char name[256];
int weight;
int age;
};
在保持类的对象的内存布局不变的情况下,我们可以将dog类的定义变换为:
typedef struct _dog dog;
struct _dog
{
/*base*/
animal base;
/*private data*/
int age;
};
两个dog类的定义在内存布局上是完全一致的。因此,只要将dog类对象的指针强制转型为animal类的指针,那么animal类的成员函数就可以访问或操作 dog类对象了,如下:
dog* my_dog = lw_new(dog_klass);
animal_set_weight(ANIMAL(my_dog), 40);
int weight = animal_get_weight(ANIMAL(my_dog));
lw_delete(my_dog);
在C++中创建子类的对象时,要先调用父类的构造函数,然后再调用子类的函数;删除子类对象时,要先调用子类的析构函数,然后再调用父类的析构函数。
animal类是dog类的父类,在定义dog类信息时,要将dog类信息中的super属性初始化为animal类信息的地址,如下:
static klass_info local_dog_klass =
{
animal_klass,
"dog_klass",
sizeof(dog),
dog_ctor,
dog_dtor,
};
在实现dog的构造函数时,就可以使用类信息的super成员获取其父类的构造函数地址,如下:
static dog* dog_ctor(dog* self)
{
((voidf)(klass_of(self)->super->ctor))(self);
self->age = 0;
return self;
}
klass_of函数的功能是获取对象的类信息地址。可以看出dog类的构造函数是先调用父类的构造函数,然后再初始化子类的属性。类似,析构函数的实现如下:
static dog* dog_dtor(dog* self)
{
((voidf)(klass_of(self)->super->dtor))(self);
return self;
}
目前为止,我们都是使用结构体初始化的方式来初始化类信息,这种方式有代码重复、容易犯错,难于维护的缺点,例如,animal类信息的初始化在animal.c中已经实现过,定义dog类时在dog.c中又要再次实现,如果再定义其他继承自animal类的子类,则还要实现。按顺序初始化结构体这种做法本身就很容易犯错,如果这样的代码到处都是,那么维护难度就可想而知了。
所以我们考虑使用类信息初始化函数来初始化类信息,即专门定义一个函数用来初始化类信息,该函数在程序运行后,第一次创建该类的对象时由lw_new函数调用。在类信息结构体中新加入一个属性,即类信息初始化函数的地址,如下:
typedef struct _klass_info klass_info;
struct _klass_info
{
void* init; /*initialize function*/
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*/
};
init属性在定义静态类信息结构体对象时初始化,其他属性在init指向的函数中初始化,以animal类为例:
static animal_klass_info local_animal_klass = {animal_init};
animal_klass_info* animal_klass = &local_animal_klass;
void animal_init(void)
{
if(animal_klass->init)
{
animal_klass->init = NULL;
animal_klass->super = NULL;
animal_klass->name = "animal_klass";
animal_klass->size = sizeof(animal);
animal_klass->ctor = animal_ctor;
animal_klass->dtor = animal_dtor;
}
}
lw_new函数修改如下:
void* lw_new(void* klass)
{
klass_info* kls = KLASS(klass);
if(kls->init)
{
((init_fun)kls->init)();
}
void* p = malloc(kls->size);
*((klass_info**)p) = kls;
return ((voidf)(kls->ctor))(p);
}
类信息初始化函数中会将类信息结构体的init属性置空,所以该函数只会被调用一次,即第一次创建该类的对象时调用。
子类的类信息初始化函数可以直接调用父类的类信息初始化函数,以dog类为例:
void dog_init(void)
{
if(dog_klass->init)
{
animal_init();
memcpy(dog_klass, animal_klass, sizeof(animal_klass_info));
dog_klass->super = animal_klass;
dog_klass->name = "dog_klass";
dog_klass->size = sizeof(dog);
dog_klass->ctor = dog_ctor;
}
}
至此我们实现了类继承,并使用类信息初始化函数来初始化类信息。
这里是相关代码。
下篇文章我们将讨论多态。
浙公网安备 33010602011771号