继承
继承就是当创建一个类时,不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。
举个例子:人是动物,人具有动物的行为和属性,但人也有动物所不具备的行为和属性。
动物 | 行为 | 属性 |
---|---|---|
会动 | 体力 |
人 | 行为 | 属性 |
---|---|---|
会动 | 体力 | |
会学习 | 智力 |
继承的实现
本实现使用了组合的方式,即把基类作为派生类的成员变量之一。
但是一定要将基类放在派生类首部,这要基类和派生类的内存首部分布才一致。
/* 基类 */
typedef struct _animal_t animal_t; /* 动物类 */
struct _animal_t
{
unsigned int stamina; /* 体力 */
};
/* 派生类 */
typedef struct _human_t human_t; /* 人类 */
struct _human_t
{
animal_t ancestor; /* 继承基类 */
unsigned int intelligence; /* 智力 */
};
在派生类中的构造函数需要调用基类的构造函数用于初始化,然后再将构造函数创建的基类对象拷贝到派生类中。
但由于是浅拷贝,即只拷贝了地址,没有拷贝地址的内容。所以要在派生类中加入基类指针成员变量,用于在析构函数释放内存。
typedef struct _human_t human_t;/* 人类 */
struct _human_t
{
animal_t ancestor; /* 继承基类 */
animal_t* p_ancestor; /* 用于调用析构函数 */
unsigned int intelligence; /* 智力 */
};
human_t* human_born(void); /* 构造函数 */
void human_die(human_t* this); /* 析构函数 */
human_t* human_born(void)
{
human_t* this = malloc(sizeof(human_t));
/* 将构造好的基类复制 */
this->p_ancestor = animal_born();
memcpy(&(this->ancestor), this->p_ancestor, sizeof(animal_t));
this->intelligence = 60;
printf("human_born: intelligence = %d\n", this->intelligence);
return this;
}
void human_die(human_t* this)
{
printf("human_die\n");
animal_die(this->p_ancestor);
free(this);
}
由于在c语言下没有类型检查功能,在调用创建的类函数时就十分不安全,在基类加入基类类型名和类大小成员变量可以解决这个问题。
基类类型名用于判断调用函数对象是否继承自该类,而类大小用于判断调用函数对象是基类还是派生类。(该方法只适用于线性继承,若有更合适的方法,请在评论区留下你宝贵的建议。)
typedef struct _animal_t animal_t; /* 动物类 */
struct _animal_t
{
char* type_name; /* 基类类型名 */
unsigned int class_size; /* 类大小 */
unsigned int stamina; /* 体力 */
};
void act(void* this); /* 动 */
typedef struct _human_t human_t;/* 人类 */
struct _human_t
{
animal_t ancestor; /* 继承基类 */
animal_t* p_ancestor; /* 用于调用析构函数 */
unsigned int intelligence; /* 智力 */
};
void learn(void* this);
void act(void* this)
{
animal_t* animal_this = (animal_t*)this;
char* type_name = animal_this->type_name;
/* 判断调用函数对象是否继承自该类 */
if(0 == strcmp(type_name,