1、纯虚函数和抽象类
(1)、只要当类中拥有纯虚函数,无论这个类有没有成员变量,这个类都是一个抽象类,抽象类是不能够实例化的。
(2)、当一个普通的类,继承自抽象类的时候,一定要重写父类中的所有纯虚函数,否则这个类仍然是抽象类。
(3)、被当做父类的抽象类,通常被当做API接口被提供,也常常使用抽象类的指针指向实例化的对象(父类指针指向子类对象)。
下面是一个简单的纯虚函数构成抽象类的一个例子
#include <iostream>
#include <cstring>
#include <memory>
using namespace std;
//人类
class people
{
public:
people(string name)
{
this->m_name = name;
}
virtual ~people()
{
}
virtual void ActShow() = 0;//不同人群的行为模式不同
protected:
string m_name; //每个人都有姓名
};
//学生
class student : public people
{
public:
student(string name):people(name)
{
}
void ActShow()
{
cout<<"student "<<m_name<<" 每天去学校学习"<<endl;
}
};
//工人
class workers : public people
{
public:
workers(string name):people(name)
{
}
void ActShow()
{
cout<<"workers "<<m_name<<" 每天去工地搬砖"<<endl;
}
};
int main(void)
{
people *p1 = new student("zhang3");
p1->ActShow();
people *p2 = new workers("li4");
p2->ActShow();
return 0;
}
其中的people类就是一个抽象类,student类和worker类则是抽象类的一个具体实现,people类中的纯虚函数ActShow完全由子类来实现。
这里就涉及到我们的一个,在c++多态程序设计的设计思路:向抽象层靠拢的程序设计思想
业务层的代码程序设计,面向抽象类编程,降低代码耦合度。依赖倒转原则-----上层业务层和底层实现层,向中间抽象层靠拢。
main函数就相当于是我们的一个业务需求,也就是业务层。当有很多业务的时候,也会封装成接口,按照逻辑顺序放到main中执行。业务层往往不关心,底层到底是怎么实现的,只关心抽象层。比如此例中,我们业务层只需要给我两个人群,一个是学生,一个是工人,并且打印出来他们的日常行为模式。
抽象层就根据业务需求,提供一个人群类,在类中提供一个打印该人群日常行为模式的方法。抽象层也不关心底层怎么实现。
底层实现层,也就是对应学生类和工人类的具体实现。
2、C语言函数指针的语法和意义
我们常说c语言是一门高级语言,c++的底层是C语言来实现的,那么接下来我们来讨论一下,C语言是如何实现c++的多态的。
首先我们要知道学习C语言有两大块内容是绕不过去的,一个是指针,另一个是函数指针。
先回顾一下三种函数指针的写法
#include <iostream>
#include <cstring>
#include <memory>
using namespace std;
//方式1
typedef void (FUNC)(int, int);
//方式2
typedef void (*PFUNC)(int, int);
void old_func(int a, int b)
{
cout<<"this is old_func..."<<endl;
}
int main(void)
{
FUNC *fp1 = NULL;
fp1 = old_func;
fp1(1, 2);
PFUNC fp2 = NULL;
fp2 = old_func;
fp2(10, 20); //在调用的时候 不用考虑取*取&的问题,C语言编译器对这里优化了
//方式3
void (*fp3)(int, int) = NULL;
fp3 = old_func;
fp3(100, 200);
return 0;
}
执行结果没有问题,打印了三次this is old_func...
可以使用函数指针,来构成我们自己的C语言的抽象类。比如我们的抽象需求就是,完成f的一次功能调用,并给该实现传递两个参数。该功能可以是old_func,可以是其他的函数,只要满足形参列表是(int, int)且返回值是void,就可以。这样就像类似于c++中的纯虚函数
#include <iostream>
#include <cstring>
#include <memory>
using namespace std;
//方式1
typedef void (FUNC)(int, int);
//方式2
typedef void (*PFUNC)(int, int);
void old_func(int a, int b)
{
cout<<"this is old_func..."<<endl;
}
void new_func(int a, int b)
{
printf("this is new_func...[a:%4d, b:%4d]\n", a, b);
}
//自定义的抽象框架,形参列表有三个,第一个是函数指针void(int,int),第2第3个参数就是int,int用来做函数指针的形参
//函数指针可以使用三种方式中的任意一种
int my_abstract_frame(PFUNC fp, int a, int b)
{
printf("my abstract frame...\n");
fp(a, b);
printf("do something else...\n -------------------\n");
return 0;
}
int main(void)
{
#if 0
//方式3
void (*fp3)(int, int) = NULL;
fp3 = old_func;
fp3(100, 200);
#endif
my_abstract_frame(old_func, 666, 999);
my_abstract_frame(new_func, 1000, 2000);
return 0;
}
上面例子中的my_abstract_frame,就相当于是c++中的抽象类。我们在my_abstract_frame的定义中规定了传入的函数指针类型PFUNC。old_func和new_func,就相当于是子类的具体实现。当我们用不同的实现,做不同的事的时候,只要把对应类型的函数名传进去就可以了。
这里也体现了一点C语言框架的思想。C程序工程业务框架的搭建者,只要定义好框架内做的事请,并规定好调用执行方法的类型,老程序员完成的旧业务old_func,新程序员不用关心old_func如何实现,只用关心自己的业务功能实现new_func,并满足方法的执行类型,塞到原有的框架里就可以了。c++中的抽象类实现,也许就是用的函数指针,这里不做深入的研究。
这样也体现了,向抽象层靠拢的程序设计思想,业务层规定好框架,底层实现层完成具体功能。