序:
前段时间,去复试淘米的面试,被问到了怎么用C语言实现C++中的多态,当时,只是模模糊糊的知道,使用函数指针可以达到C++多态的效果,但是,具体怎么实现,却还是不清楚。最终面试官让我说了一下C++中的多态。虽然知道被挂在了二面,但是,却感觉并没有什么,每一次的失败,都是为了最后一次的成功积蓄力量,回报越晚,回报越大。这不,因为这件事情,我知道了怎么用C语言实现C++的多态。
正文:
1.首先,弄清楚一个问题,C++多态。
(注:摘自C和C++程序员面试秘笈)
多态:同一操作作用于不同对象,可以有不同的解释,产生不同的执行结果。有两种类型的多态性:
<1>编译时的多态性:编译时的多态性是通过重载函数来实现的。对于非虚的成员函数来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。
<2>运行时的多态性:运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。C++中,运行时的多态性通过虚成员实现。
示例:
这里写代码片
#include <iostream>
using namespace std;
class A
{
public:
virtual void f()
{
cout << "A::f()" << endl;
}
};
class B:public A
{
public:
virtual void f()
{
cout << "B::f()" << endl;
}
};
class C:public B
{
public:
void f()
{
cout << "C::f()" << endl;
}
};
void test(A &a) //基类的指针或引用
{
a.f(); //运行时多态
}
int main()
{
A a;
B b;
C c;
test(a);
test(b);
test(c);
return 0;
}
2.C语言实现C++中的多态
(摘自:http://blog.csdn.net/dai_jing/article/details/38232641)
这里写代码片
<1>基类头文件
#ifndef _ANIMAL_H_
#define _ANIMAL_H_
//动物的行为
typedef struct animal_ops_s_
{
void (*eat)(char *food); //吃什么食物
void (*walk)(int steps); //走多少步
void (*talk)(char *msg); //说什么
}animal_ops_t;
//动物类,所有动物的基类(抽象类)
typedef struct animal_s_
{
char *name; //动物的名称
animal_ops_t *animal_ops; //动物的行为
}animal_t;
//基类的构造函数,需要显示调用
extern animal_t *animal_init(char *name);
//基类的相关操作
extern void animal_eat(animal_t *animal,char *food);
extern void animal_walk(animal_t *animal,int steps);
extern void animal_talk(animal_t *animal,char *msg);
//基类的析构函数,需要显示调用
extern void animal_die(animal_t *animal);
#endif
<1>基类的实现
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "animal.h"
//基类的构造函数,需要显示调用
animal_t *animal_init(char *name)
{
assert(name != NULL);
size_t name_len = strlen(name);
animal_t *animal = (animal_t *)malloc(sizeof(animal_t));
memset(animal,0,sizeof(animal));
animal->name = (char *)malloc(name_len + 1);
memcpy(animal->name,name,name_len+1);
animal->animal_ops = (animal_ops_t *)((char *)animal+name_len + 1);
animal->animal_ops->eat = NULL;
animal->animal_ops->walk = NULL;
animal->animal_ops->talk = NULL;
return animal;
}
//基类相关的操作
void animal_eat(animal_t *animal,char *food)
{
animal->animal_ops->eat(food);
}
void animal_walk(animal_t *animal,int steps)
{
animal->animal_ops->walk(steps);
}
void animal_talk(animal_t *animal,char *msg)
{
animal->animal_ops->talk(msg);
}
//基类的析构函数,需要显示调用
void animal_die(animal_t *animal)
{
return ;
}
<2>汪星人头文件
#ifndef _DOG_H_
#define _DOG_H_
#include "animal.h"
typedef struct dog_s_ dog_t;
struct dog_s_
{
animal_t base; //继承自animal基类
};
extern dog_t *dog_init();
extern void dog_die(dog_t *dog);
#endif
<2>汪星人实现
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dog.h"
static void eat(char *food);
static void walk(int steps);
static void talk(char *msg);
dog_t *dog_init()
{
dog_t *dog = (dog_t *)malloc(sizeof(dog_t));
animal_t *animal = (animal_t *)animal_init("hello-dog");
memcpy(&(dog->base),animal,sizeof(animal_t));
dog->base.animal_ops->eat = eat;
dog->base.animal_ops->walk = walk;
dog->base.animal_ops->talk = talk;
animal_die(animal);
return dog;
}
void dog_die(dog_t *dog)
{
assert(dog != NULL);
free(dog);
dog = NULL;
}
static void eat(char *food)
{
printf("I'm a dog,I eat %s\n",food);
}
static void walk(int steps)
{
printf("I'm a dog, I can jump %d steps\n",steps);
}
static void talk(char *msg)
{
printf("I'm a dog,I talk my language %s\n",msg);
}
<3>喵星人头文件
#ifndef _CAT_H_
#define _CAT_H_
#include "animal.h"
typedef struct cat_s_ cat_t;
struct cat_s_
{
animal_t base; //继承自animal基类
};
extern cat_t *cat_init();
extern void cat_die(cat_t *cat);
#endif
<3>喵星人实现
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "cat.h"
static void eat(char *food);
static void walk(int steps);
static void talk(char *msg);
cat_t *cat_init()
{
cat_t *cat = (cat_t *)malloc(sizeof(cat_t));
animal_t *animal = (animal_t*)animal_init("hello-cat");
memcpy(&(cat->base),animal,sizeof(animal_t));
cat->base.animal_ops->eat = eat;
cat->base.animal_ops->walk = walk;
cat->base.animal_ops->talk = talk;
return cat;
}
void cat_die(cat_t *cat)
{
assert(cat != NULL);
free(cat);
cat = NULL;
}
static void eat(char *food)
{
printf("I'm a cat,I eat %s\n",food);
}
static void walk(int steps)
{
printf("I'm a cat,I can jump %d steps\n",steps);
}
static void talk(char *msg)
{
printf("I'm a cat,I talk my language %s\n",msg);
}
<4>主函数
#include <stdio.h>
#include "animal.h"
#include "dog.h"
#include "cat.h"
int main()
{
cat_t *cat = cat_init();
dog_t *dog = dog_init();
//dog测试
animal_eat(dog,"bones");
animal_walk(dog,5);
animal_talk(dog,"wang wang wang...");
//cat测试
animal_eat(cat,"fish");
animal_walk(cat,3);
animal_talk(cat,"miao miao miao...");
cat_die(cat);
dog_die(dog);
return 0;
}
<5>程序运行结果截图:
3.感悟
实际上,在C语言中,模仿C++实现多态的过程中,对于构造和析构函数都是要显示的进行调用,对于类的成员函数,实际上是通过结构体内部封装的函数指针完成的。而对于从基类继承而来的派生类,它的虚表的确定,实际上是在自身的构造函数中显示的调用基类的构造函数,然后复制基类的构造函数的内容,之后,可以在自己的类中添加一些其他的操作,而对于自己本身的函数成员,在本模块内有效,声明为静态函数,这样就可以避免命名的冲突问题。总之,在C语言中要想实现多态,函数指针是唯一法宝。