继承
继承就是当创建一个类时,不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。
举个例子:人是动物,人具有动物的行为和属性,但人也有动物所不具备的行为和属性。
动物 | 行为 | 属性 |
---|---|---|
会动 | 体力 |
人 | 行为 | 属性 |
---|---|---|
会动 | 体力 | |
会学习 | 智力 |
继承的实现
本实现使用了组合的方式,即把基类作为派生类的成员变量之一。
但是一定要将基类放在派生类首部,这要基类和派生类的内存首部分布才一致。
/* 基类 */
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, "animal_t"))
{
printf("animal_act\n");
animal_this->stamina -= 1;
printf("stamina = %d\n", animal_this->stamina);
}
else
{
printf("can't act\n");
}
}
void learn(void* this)
{
human_t* human_this = (human_t*)this;
char* type_name = human_this->ancestor.type_name;
unsigned int class_size = human_this->ancestor.class_size;
/* 判断调用函数对象是否继承自该类 */
/* 避免基类调用函数 */
if((0 == strcmp(type_name, "animal_t")) &&
(class_size >= sizeof(human_t)));
{
human_this->intelligence += 1;
printf("human_learn: intelligence+1\n");
printf("intelligence = %d\n", human_this->intelligence);
}
else
{
printf("can't learn\n");
}
}
多态
在上个例子中,人和动物都有会动的属性,但人的动作和动物的动作从表现上会有所不同。而多态就可以实现在同一个函数中,根据对象类型的不同,函数实现方式也不同。
多态的实现
c语言多态的实现,需要用到函数指针。函数名实际上是该函数代码存储空间的首地址,这个地址可以通过函数指针来存放。通过改变函数指针存储的地址就可以实现多态。
typedef struct _animal_t animal_t;
typedef void (*animal_act_t)(animal_t*); /* 函数指针类型 */
typedef struct _animal_vtable_t /* 虚函数表 */
{
animal_act_t act;
}animal_vtable_t;
struct _animal_t
{
animal_vtable_t* vt; /* 虚函数表指针存放到类中 */
unsigned int stamina;
};
animal_t* animal_born(void); /* 构造函数 */
void act(void* this); /* 函数接口 */
void animal_die(animal_t* this); /* 析构函数 */
static void animal_act(animal_t* this);
/* 动物类的虚函数表 */
static animal_vtable_t __g_animal_vtable =
{
/* 将实现函数的首地址(函数名)赋值给函数指针 */
.act = animal_act,
};
animal_t* animal_born(void)
{
animal_t* this = malloc(sizeof(animal_t));
this->vt = &__g_animal_vtable;
this->stamina = 100;
printf("animal_born: stamina = 100\n");
return this;
}
void act(void* this)
{
/* 通过虚函数表的函数指针调用实现函数 */
return ((animal_t*)this)->vt->act(this);
}
/* 实现函数 */
static void animal_act(animal_t* this)
{
if(this->stamina >= 30)
{
this->stamina -= 5;
printf("animal act: stamina-5\n");
printf("stamina = %d\n", this->stamina);
}
else
{
this->stamina += 10;
printf("animal rest: stamina+10\n");
printf("stamina = %d\n", this->stamina);
}
}
void animal_die(animal_t* this)
{
free(this);
printf("animal_die\n");
}
示例代码
该代码将结合上述继承和多态的方法实现对基类函数进行重写:
/* animal.h */
#ifndef _ANIMAL_H_
#define _ANIMAL_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct _animal_t animal_t;
typedef void (*animal_act_t)(animal_t*);
typedef struct _animal_vtable_t
{
animal_act_t act;
}animal_vtable_t;
struct _animal_t
{
char* type_name;
unsigned int class_size;
animal_vtable_t* vt;
unsigned int stamina;
};
animal_t* animal_born(void);
void act(void* this);
void animal_die(animal_t* this);
#endif /* _ANIMAL_H_ */
/* animal.c */
#include "animal.h"
static void animal_act(animal_t* this);
static animal_vtable_t __g_animal_vtable =
{
.act = animal_act,
};
animal_t* animal_born(void)
{
animal_t* this = malloc(sizeof(animal_t));
this->type_name = malloc(sizeof("animal_t"));
memcpy(this->type_name, "animal_t", sizeof(this->type_name));
this->class_size = sizeof(animal_t);
this->vt = &__g_animal_vtable;
this->stamina = 100;
printf("animal_born: stamina = 100\n");
return this;
}
void act(void* this)
{
if(0 == strcmp(((animal_t*)this)->type_name, "animal_t"))
{
return ((animal_t*)this)->vt->act(this);
}
printf("can't act\n");
}
static void animal_act(animal_t* this)
{
if(this->stamina >= 30)
{
this->stamina -= 5;
printf("animal act: stamina-5\n");
printf("stamina = %d\n", this->stamina);
}
else
{
this->stamina += 10;
printf("animal rest: stamina+10\n");
printf("stamina = %d\n", this->stamina);
}
}
void animal_die(animal_t* this)
{
free(this->type_name);
free(this);
printf("animal_die\n");
}
/* human.h */
#ifndef _HUMAN_H_
#define _HUMAN_H_
#include "animal.h"
typedef struct _human_t human_t;
typedef void (*human_learn_t)(human_t*);
typedef struct _human_vtable_t
{
human_learn_t learn;
}human_vtable_t;
struct _human_t
{
animal_t ancestor;
animal_t* p_ancestor;
human_vtable_t* vt;
unsigned int intelligence;
};
human_t* human_born(void);
void learn(void* this);
void human_die(human_t* this);
#endif /* _HUMAN_H_ */
/* human.c */
#include "human.h"
static void human_act(human_t* this);
static void human_learn(human_t* this);
static animal_vtable_t __g_animal_vtable;
static human_vtable_t __g_human_vtable =
{
.learn = human_learn,
};
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->ancestor.class_size = sizeof(human_t);
memcpy(&__g_animal_vtable, this->ancestor.vt, sizeof(animal_vtable_t));
this->ancestor.vt = &__g_animal_vtable;
this->ancestor.vt->act = human_act;
this->vt = &__g_human_vtable;
this->intelligence = 60;
printf("human_born: intelligence = %d\n", this->intelligence);
return this;
}
void learn(void* this)
{
char* type_name = ((human_t*)this)->ancestor.type_name;
unsigned int class_size = ((human_t*)this)->ancestor.class_size;
if((0 == strcmp(type_name, "animal_t")) &&
(class_size >= sizeof(human_t)));
{
return ((human_t*)this)->vt->learn(this);
}
printf("can't learn\n");
}
static void human_act(human_t* this)
{
if(this->ancestor.stamina >= 30)
{
this->ancestor.stamina -= 1;
printf("human act: stamina-1\n");
printf("stamina = %d\n", this->ancestor.stamina);
}
else
{
this->ancestor.stamina += 5;
printf("human act: stamina+5\n");
printf("stamina = %d\n", this->ancestor.stamina);
}
}
static void human_learn(human_t* this)
{
this->intelligence += 1;
printf("human_learn: intelligence+1\n");
printf("intelligence = %d\n", this->intelligence);
}
void human_die(human_t* this)
{
printf("human_die\n");
animal_die(this->p_ancestor);
free(this);
}
/* main.c */
#include "animal.h"
#include "human.h"
int main()
{
printf("\nanimal monkey:\n");
animal_t* monkey = animal_born();
act(monkey);
printf("\nanimal monkey learn:\n");
learn(monkey);
animal_die(monkey);
printf("\nhuman qaq:\n");
human_t* qaq = human_born();
act(qaq);
learn(qaq);
human_die(qaq);
system("pause");
return 0;
}
结果如下:
程序运行结果