参考:http://blog.csdn.net/hackbuteer1/article/details/7475622
多态性
指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C++支持两种多态性:编译时多态性,运行时多态性。
a、编译时多态性:通过重载函数实现
b、运行时多态性:通过虚函数和虚析构函数实现。
所谓重载,是指在同一个作用域(如同一个函数体内、同一个类内、都是全局等等)里的几个同名不同参的函数,它们之间是重载关系。
所以,首先区别于:分别位于子类和父类中的同名函数绝对不是重载关系,因为作用域不同。它们之间的关系要么是隐藏,要么是覆盖。其中覆盖,就是通过虚函数和虚析构函数实现多态后呈现出来的一种关系。
以下为例:
Father.h
#ifndef FATHER_H
#define FATHER_H
class Father
{
public:
Father();
virtual ~Father();
void fun1(int i);
void fun2(float f);
virtual void fun3(float f);
virtual void fun4(int i);
virtual void fun5();
};
#endif
Father.cpp
#include"Father.h"
#include<iostream>
using namespace std;
Father::Father()
{
cout << "Father()" << endl;
}
Father::~Father()
{
cout << "~Father()" << endl;
}
void Father::fun1(int i)
{
cout << "Father::fun1(int i)" << i << endl;
}
void Father::fun2(float f)
{
cout << "Father::fun2(float f)" << f << endl;
}
void Father::fun3(float f)
{
cout << "Father::fun3(float f)" << f << endl;
}
void Father::fun4(int i)
{
cout << "Father::fun4(int i)" << i << endl;
}
void Father::fun5()
{
cout << "Father::fun5(int i)" << endl;
}
Kid.h
#ifndef KID_H
#define KID_H
#include"Father.h"
class Kid:public Father
{
public:
Kid();
virtual ~Kid();//虚析构函数
void fun1(int i);//同名同参不加v
void fun2();//同名不同参不加v
virtual void fun3();//同名不同参加v
virtual void fun4(int i);//同名同参加v
};
#endif
Kid.cpp
#include"Kid.h"
#include<iostream>
using namespace std;
Kid::Kid()
{
cout << "Kid()" << endl;
}
Kid::~Kid()//虚析构函数
{
cout << "~Kid()" << endl;
}
void Kid::fun1(int i)//同名同参不加v
{
cout << "Kid::fun1(int i)" << i << endl;
}
void Kid::fun2()//同名不同参不加v
{
cout << "Kid::fun2(int i)" << endl;
}
void Kid::fun3()//同名不同参加v
{
cout << "Kid::fun3(int i)" << endl;
}
void Kid::fun4(int i)//同名同参加v
{
cout << "Kid::fun4(int i)" << i << endl;
}
demo.cpp
#include<iostream>
#include<stdlib.h>
#include"Father.h"
#include"Kid.h"
using namespace std;
int main()
{
Father f;
Kid k;
f.fun1(1);
f.fun2(1.1);
f.fun3(1);
f.fun4(2.2);
k.fun1(1.1);//隐藏关系
k.Father::fun1(1.1);//子类想要访问父类的fun2就必须加前缀
k.fun2();
//k.fun2(2.2);//报错,说明Kid::fun3()和Father::fun3(float)并非重载关系,而是隐藏关系
k.Father::fun2(1.1);//子类想要访问父类的fun2就必须加前缀
k.fun3();
//k.fun3(1.1);//报错,说明Kid::fun3()和Father::fun3(float)并非重载关系,而是隐藏关系
k.Father::fun3(1.1);//子类想要访问父类的fun3就必须加前缀
k.fun4(4.4);
k.Father::fun4(4.4);//这里也是隐藏关系,但是fun4可以验证另一个重要关系(见下)
k.fun5();//因为Kid类中没有重新定义fun5,所以不会被隐藏
Father f1 = k;//调用拷贝构造函数
f1.fun1(1);
//f1.Kid::fun1(1);
f1.fun2(1.1);
//f1.Kid::fun2();
//f1.fun2();//报错,因为父类中可没有子类的成员
f1.fun3(1);
//f1.Kid::fun3();
//f1.fun3();//报错
f1.fun4(2.2);//父类对象直接访问到的是父类的fun4
//f1.Kid::fun4(2.2);//报错
Father *pf2 = &k;
pf2->fun1(1);
//pf2->Kid::fun1(1);//报错
pf2->fun2(1.1);
//pf2->fun2();//报错
//pf2->Kid::fun2();//报错
pf2->fun3(1);
//pf2->fun3();//报错
//pf2->Kid::fun3();//报错
pf2->fun4(2.2);//由父类对象指针直接访问到了子类的fun4,这就是覆盖关系
pf2->Father::fun4(2.2);//可以加前缀访问父类的fun4
pf2->fun5();//如果在子类中重新定义了fun5的话,fun5也会实现多态呈现覆盖关系,但实际没有,所以多态没有实现,仍是访问了父类的fun5
Father *pf3 = new Kid;
delete pf3;
pf3 = NULL;
system("pause");
return 0;
}
运行结果:
由结果可见,父类对象f1虽然被k赋值,但无法实现多态呈现出覆盖关系。父类对象指针pf2、pf3都是指向子类对象的,可以实现多态并呈现出覆盖关系,而且只有同名同参且加virtual且在子类中重新定义了的函数fun4实现了多态呈现出覆盖关系;在释放pf3时,由于析构函数加上了virtual成为虚析构函数,所以也实现了多态,即释放实际指向子类对象的父类对象时会实际释放掉指向的子类对象(所以先调用子类析构函数后调用父类析构函数)。
综上可得:
要实现多态,则:
1.析构函数直接加上virtual关键字成为虚析构函数。
2.分别属于子类、父类的同名同参(即完全相同)的函数加上virtual关键字,并且一定要在子类中重新定义。
除上述两种外,分别属于子类父类的同名函数(不管参数、virtual),都是隐藏关系。(注意不可能是重载!)