关于多态的学习总结(主要指的是虚函数的部分)
多态的最大特点是:“一个操作,不同结果”。而构成它的必要条件则是,赋值兼容规则和函数的虚特性;
赋值兼容规则指的是,在公有派生的前提下(C++中由基类到子类叫“派生”,反之,叫继承),基类指针可以指向子类的对象,具体点说,基类指针指向的是派生类的基类的部分(子类对象在创建之前,会先创建基类对象,所以,一个子类对象包含基类对象的部分和派生类的部分)。
那么问题来了,既然基类指针指向的仅仅是基类的部分,那么也就是说,若派生类有一个和基类一样的函数,那么,该指针将不能直接访问该成员函数。因此,C++引进了虚函数这个东西,用来解决这个问题。
对于虚函数来说,第一个肯定是要有virtual这个关键字,那么该成员函数就有了虚特性,那么要把它的虚特性表现出来这显然是不够的,C++中规定,该虚函数要表现出虚特性,必须要在它的子类中也有一样的成员函数。(“一样”指的是函数的返回值,函数名, 函数的参数列表即参数的个数和类型必须相同)。只有满足这些函数的虚特性就能表现出来了,而它的派生类中这个一样的函数被自动也说明为virtual。
例如,有一个基类Base,它有三个派生类,D1,D2和D3,他们都有一个一样的fun函数,并在基类中用virtual说明了它;
那么,
<strong> Base * p;
p = new D1();
p->fun();
p = new D2();
p->fun();
p = new D3();
p->fun();</strong>
也就是说,由于兼容性规则的存在,那么,对于基类指针p它可以指向不同的派生类对象,又由于函数的虚特性特点,它可以访问派生类中不同的fun();那么问题又来了,既然基类的fun()既然说明成了虚函数,那么通过多态是访问不到的。那么,基类的fun()的唯一作用就是想通过它来访问派生类的fun(),这就类似一个水管的接口,水流可以通过这个接口可以流到它的分支里,在C++中,这个基类的fun()就被叫做“类的接口”,既然是接口,那就是只是想通过它来访问某个子类。因此,对于虚函数C++又提出了两个概念:“空虚函数”和“纯虚函数”,即
<strong> virtual void fun(){} //空虚函数
virtual void fun() = 0; //纯虚函数</strong>
值得注意的是含有纯虚函数的类是不能创建对象的,那么,如果还想实现原来基类的功能,就可以直接再写一个派生类,用它来实现原来fun()的功能;虽然,可以通过匹配的角度访问它,即就是创建一个Base类的对象,用p指针指向它,那么,就可以访问它的Base的fun()了,但是,这明显的破坏了它的多态性,而多态性的提出,就是为了在增加功能时,只是采用扩展,而不采用修改类里的东西,来修改该功能的扩充,即“开闭原则”;
下面是一个具体实例:
<span style="font-size:24px;"><strong>//工厂模式之“媳妇做饭问题 ”
#include <iostream>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
//饭类
class fan {
public:
virtual void dofan() = 0; //做饭类的接口, 派生类的dofan函数被自动说明为virtual类型
};
//糖拌西红柿
class XHS : public fan{
public:
void dofan() {
cout << "糖拌西红柿\n";
}
};
//棍棍面
class GGM : public fan{
public:
void dofan() {
cout << "棍棍面\n";
}
};
//羊肉泡
class YRP : public fan{
public:
void dofan() {
cout << "羊肉泡\n";
}
};
//媳妇
class wife{
public:
fan * p;
wife (int n){
switch (n){
case 1: p = new XHS();
break;
case 2: p = new GGM();
break;
case 3: p = new YRP();
break;
default:
cout << "抱歉,不会做你要吃的饭...\n";
}
}
};</strong></span>
如果按照上面的模式写,所有的饭都让一个媳妇来做,看起来好像满足了“开闭原则”,但是,实际上随着我们生活水平的不断提高,我还可能想吃米饭炒菜,虽然对于饭类来说,只是添一个米饭炒菜类就行,但对于媳妇类来说,就违反了“开闭原则”了;因此我们想到,对客户端即媳妇类使用多态,代码如下:
<span style="font-size:24px;"><strong>//工厂模式之“媳妇做饭问题 ”
#include <iostream>
using namespace std;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
//饭类
class fan {
public:
virtual void dofan() = 0; //做饭类的接口, 派生类的dofan函数被自动说明为virtual类型
};
//糖拌西红柿
class XHS : public fan{
public:
void dofan() {
cout << "糖拌西红柿\n";
}
};
//棍棍面
class GGM : public fan{
public:
void dofan() {
cout << "棍棍面\n";
}
};
//羊肉泡
class YRP : public fan{
public:
void dofan() {
cout << "羊肉泡\n";
}
};
//米饭炒菜
class MFCC : public fan{
public:
void dofan() {
cout << "米饭炒菜\n";
}
};
//媳妇
class wife {
public:
virtual void SWife(int n) = 0; //媳妇类的接口
};
//大媳妇
class DWife : public wife{
public:
fan * p;
void SWife (int n){
switch (n){
case 1: p = new XHS();
break;
case 2: p = new GGM();
break;
case 3: p = new YRP();
break;
default:
cout << "抱歉,不会做你要吃的饭...\n";
}
}
~DWife(){
p->dofan();
delete p;
}
};
//二媳妇
class EWife : public wife{
public:
fan * p;
void SWife(int n){
p = new MFCC();
}
~EWife(){
p->dofan();
delete p;
}
};</strong></span>
这次看代码貌似好了,但可以试想一下,万一要是大媳妇生气了,不做饭了,那么凉拌西红柿,棍棍面以及羊肉泡就吃不到了,这就是违反了软件工程所提倡的“高内聚,低耦合”原则,具体来说,就是代码的那么解决的办法,就是降低它的耦合度,也就是说,一个媳妇只做一种饭,想吃下一个饭,只要派生出该饭的类,和它所对应的媳妇;
代码如下:
<span style="font-size:24px;"><strong>//工厂模式之“媳妇做饭问题 ”
#include <iostream>
using namespace std;
//饭类
class fan {
public:
virtual void dofan() = 0; //做饭类的接口, 派生类的dofan函数被自动说明为virtual类型
};
//糖拌西红柿
class XHS : public fan{
public:
void dofan() {
cout << "糖拌西红柿\n";
}
};
//棍棍面
class GGM : public fan{
public:
void dofan() {
cout << "棍棍面\n";
}
};
//羊肉泡
class YRP : public fan{
public:
void dofan() {
cout << "羊肉泡\n";
}
};
//米饭炒菜
class MFCC : public fan{
public:
void dofan() {
cout << "米饭炒菜\n";
}
};
//媳妇
class wife {
public:
virtual void SearchWife() = 0; //媳妇类的接口
};
//大媳妇
class DWife : public wife{
public:
fan * p;
void SearchWife(){
p = new XHS();
}
~DWife(){
p->dofan();
delete p;
}
};
//二媳妇
class EWife : public wife{
public:
fan * p;
void SearchWife(){
p = new GGM();
}
~EWife(){
p->dofan();
delete p;
}
};
//三媳妇
class SWife : public wife{
public:
fan * p;
void SearchWife(){
p = new YRP();
}
~SWife(){
p->dofan();
delete p;
}
};
//四媳妇
class SiWife : public wife{
public:
fan * p;
void SearchWife(){
p = new YRP();
}
~SiWife(){
p->dofan();
delete p;
}
};
//五媳妇
class WWife : public wife{
public:
fan * p;
void SearchWife(){
p = new MFCC();
}
~WWife(){
p->dofan();
delete p;
}
}; </strong></span>
经过这个问题的练习,不难发现多态的重要性,以及它的应用,以上就是本人对多态的理解。