C语言也能面向对象(三)——通用的new和delete

Technorati 标签: c, c语言, 面向对象, oo, object-oriented

本文版权所有,转载请注明出处和作者联系方式。
作者:孙华明
联系方式: 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函数创建和删除任何类的对象,并且自动调用其构造函数和析构函数了,本篇文章相关代码可以到这里下载。

下篇文章我们将讨论继承。

你可能感兴趣的:(C语言也能面向对象(三)——通用的new和delete)