用C语言实现C++继承与多态

继承

继承就是当创建一个类时,不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类
举个例子:人是动物,人具有动物的行为和属性,但人也有动物所不具备的行为和属性。

动物 行为 属性
会动 体力
行为 属性
会动 体力
会学习 智力

继承的实现

本实现使用了组合的方式,即把基类作为派生类的成员变量之一。
但是一定要将基类放在派生类首部,这要基类和派生类的内存首部分布才一致。

/* 基类 */
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;
}

结果如下:


程序运行结果
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。