1.函数重载
由静态联编支持的多态性称为编译时的多态性或静态多态性,也就是说,确定同名操作的具体操作对象的过程是在编译过程中完成的。在C++中,可以用函数重载和运算符重载来实现编译时的多态性。
2.虚函数
由动态联编支持的多态性称为运行时的多太性或动态多态性,也就是说,确定同名操作的具体操作对象的过程是在运行过程中完成的。在C++中,可以用虚函数来实现运行时的多态性。
2.1.虚函数定义
虚函数的定义是在基类中进行的,即把基类中需要定义为虚函数的成员函数声明为virtual。当基类中的某个成员函数被声明为虚函数后,就可以在派生类中被重新定义。在派生类中重定义时,其函数原型,包括返回类型、函数函数名、参数个数和类型、参数的顺序都必须与基类中的原型完全一致。
定义形式为:
virtual <函数类型><函数名>(参数表)
{
函数体
}
例如:解决博文多态概念时的问题代码如下:
#include<iostream>
using namespace std;
class Animal
{
public:
void sleep()
{
cout<<"Animal Sleep"<<endl;
}
virtual void breathe()//加上virtual
{
cout<<"Animal Breathe"<<endl;
}
};
class Fish:public Animal
{
public:
void breathe()
{
cout<<"Fish Bubble"<<endl;
}
};
int main()
{
Fish fs;
Animal *an=&fs;
an->breathe();
system("pause");
}
只是加了一个virtual问题就解决了。输出结果为:
事实上,当将基类中的成员函数breathe()声明为virtual即虚函数时,编译器在编译的时候发现Animal类中有虚函数,此时编译器会为每个包含虚函数的类创建一个虚表,该表是一个一维数组,在这个数组中存放每个虚函数的地址。上述代码中,Animal类和Fish类都包含了一个虚函数breathe(),因此编译器会为这两个类分别建立一个虚表,如下图所示。
类Animal和类Fish的虚表
在上述代码中,当Fish类的fs对象构造完毕后,其内部的虚表指针也就被初始化为指向Fish类的虚表。在转换后,调用an->breathe(),由于an实际指向的是Fish类的对象,该对象内部的虚表指针指向的是Fish类的虚表,因此最终调用的是Fish类的breathe()函数。
使用派生对象指针时应注意的问题:
1.声明为指向基类对象的指针可以指向它的公有派生类的对象,但不允许指向它的私有派生类的对象。
2.允许声明为指向基类对象的指针指向它的公有派生类的对象,但不允许将一个声明为指向派生类对象的指针指向基类的对象。
3.声明为指向基类对象的指针,当其指向它的公有派生类的对象时(满足第1条),只能直接访问派生类中从基类继承下来的成员,不能直接访问公有派生类中定义的成员。要想访问其公有派生类中的成员,可将基类指针用显式类型转换方式转换为派生类指针。例如:
#include<iostream>
using namespace std;
class B
{
public:
void vf()
{
cout<<"This is class B"<<endl;
}
};
class D:public B
{
public:
void vf()
{
cout<<"This is class D"<<endl;
}
};
int main()
{
B b,*pb;
D d,*pd;
pb= &b;
pb->vf();
pb=&d;//满足第1第2条。
pb->vf();
// pd=&b;//违背第2条。invalid conversion from `B*' to `D*'
// pd->vf();
pd=(D*)&b;//满足第3条。
pd->vf();
pd=&d;
pd->vf();
system("pause");
}