之前讲了如何用C中的结构体和函数指针模拟封装特性。
下面进入下一个特性,继承。
我们回顾一下继承的概念,继承中的两个重要关系就是基类和派生类。派生类拥有基类的所有特征,并且可定义新的特征或者对基类的一些特性进行重定义。
接着上次讲的出现职员的例子,现在我们想在原来基础上增加一个经理类。经理类就是职员类的一个子类。而且我们预期做到经理不仅仅有职员的所有成员和方法,还有自己的方法:
void main()
{
Employee a;
a.Print();
Manager m; //创建经理类
m.Print(); //这是原来职员类的方法
m.ShowNum();//这是经理类特有的方法
}
那么在C中要如何实现呢?
我们仔细考虑,整个子类的数据在内存中分布情况。
我的理解是前面部分存放的是基类的部分,后面部分存放子类的部分。也就是说,如果不看一个子类的子类部分,我们可以完全把它当做一个基类来看。
需要注意继承与聚合这一概念的区别,聚合指的也是在一个结构体或者类中,存在另外的结构体或者类,比如飞机类中有发动机类、引擎类,但是并不是说飞机就是发动机或者引擎,而继承的概念是严格的is-a的关系,比如经理就是职员。
在设计C语言继承性的时候,我们需要做的就是把基础数据放在继承的结构的首位置即可。
这样,不管是数据的访问、数据的强转、数据的访问都不会有什么问题。也就是说,实际上是一种继承关系。
typedef struct Manager
{
Employee base;
void (*ShowNumOfEmp)(conststruct Manager*);//包含函数指针,用来访问这个函数
}Manager;
在构造函数中首先初始化基类成员,再初始化子类成员(这里假设有个子类函数指针)
//构造函数
ManagerManagerInit(char *name,intsalay,int num_of_emp)
{
Manager ret;
Employee base=EmployeeInit(name,salay);
ret.base= base;
ret.empnum=num_of_emp;
ret.ShowNumOfEmp=ShowNumOfEmp;
return ret;
}
以后,当我们需要把子类作为基类使用,只要把子类做一下强制类型转换,就可以。
当然,苛刻的C编译器不会允许你将2个看起来毫不相关的结构体来转换。
不要忘了我们强大的指针,有了它没有不可能。
我们制作一个宏,功能是通过制作强制把任何一个类型强制转换成基类。
#define GetEmployee(m) (*((Employee*)&m))
int main()
{
Employeea=EmployeeInit("Li Hua",1000);
Managerm=ManagerInit("XiaoMeng",10000,15);
a.Print(&a);
GetEmployee(m).Print(&GetEmployee(m));
m.ShowNumOfEmp(&m);
EmployeeDestructor(a);
ManagerDestructor(m);
return 0;
}
运行结果:
本文介绍如何在C语言中模拟面向对象编程中的继承特性。通过结构体和函数指针,可以实现基类与派生类之间的关系,以及派生类对基类方法的重写。文章还提供了一个具体的经理类继承自职员类的例子。
6670

被折叠的 条评论
为什么被折叠?



