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

原创
10/18 03:53
阅读数 122

继承

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

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

继承的实现

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

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

animal_t* animal_born(void)
{
  animal_t* this = malloc(sizeof(animal_t));
	
  /* 申请内存存放虚函数表 */
  this->vt = malloc(sizeof(animal_vtable_t));

  /* 将实现函数的首地址(函数名)赋值给函数指针 */
  this->vt->act = animal_act;
  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->vt);/* 释放虚函数表的空间 */
  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);

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 = malloc(sizeof(animal_vtable_t));

  this->vt->act = animal_act;
  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");
}

void animal_die(animal_t* this)
{
  free(this->type_name);
  free(this->vt);
  free(this);
  printf("animal_die\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);
  }
}
/* 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);

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);
  this->ancestor.vt->act = human_act;
	
  this->vt = malloc(sizeof(human_vtable_t));
  this->vt->learn = human_learn;
	
  this->intelligence = 60;

  printf("human_born: intelligence = %d\n", this->intelligence);

  return this;
}

void human_die(human_t* this)
{
  free(this->vt);
  printf("human_die\n");
	
  animal_die(this->p_ancestor);
  free(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);
}
/* 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;
}

结果如下: 程序运行结果

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部