多态是C++面向对象三大基本特性之一(封装,继承,多态)。多态相对于其它两个特性是相对比较复杂的。
多态又分为静态多态和动态多态,通俗点的解释就是一种表像多种形态(纯粹个人理解)。
静态多态又称编译期多态,即在系统编译期间就可以确定程序将要执行哪个函数。
例如,函数重载,运算符重载
动态多态是利用派生类和虚函数相结合的形式实现运行时的多态,即在系统编译的时候并不知道程序将要调用哪一个函数,只有在运行到这里的时候才能确定接下来会跳转到哪一个函数。
静态多态和动态多态本质的区别:
静态多态----地址早绑定,编译阶段确定函数地址;
动态多态----地址晚绑定,运行阶段确定函数地址;
记得曾经有位老师出过下面一段代码,大概的意思是,每个动物都有自己独特的叫声,有个父类(Animal),有个子类(Dog),请问以下代码能实现狗叫吗?
图1
正确答案是:
这个案例让我终生受用,至少时至今日,我一直都记得一定要重写父类的虚函数,否则是发生不了多态的,下面给出C++中多态的条件
C++中发生多态所必须满足条件:
- 有父子的继承关系
- 子类重写父类的虚函数
- 父类的指针或者引用,指向子类的对象
最后给出正确的代码示例
#include <iostream>
#include <string>using namespace std;
class Animal
{
public:
void virtual Speak()
{
cout << "Now, Animal is Speaking!" << endl;
}
};class Dog: public Animal
{
public:
void Speak()
{
cout << "Now, Dog is Speaking! Woof,Woof..." << endl;
}
};void Speaking(Animal &animal) //Animal &animal = myDog;
{
animal.Speak();
}void test()
{
Dog myDog;
Speaking(myDog);
}int main()
{
test();
system("pause");
return 0;
}
但是我也是工作了若干年后,才有了进一步的了解,可以利用Visual Studio自带的开发者命令提示(Developer Command Prompt for VS2013)
- 进入到项目文件所在的目录,例如我自己定义的工作目录是(D:\workspace\C++\Polymorphism\Polymorphism), 并确定该文件是否存在
- ,这里我的文件名也是Polymorphism.cpp
- 使用命令 cl /d1 reportSingleClassLayoutAnimal Polymorphism.cpp
reportSingleClassLayout ----报告单个类布局(很直白的翻译)
Animal----类名(一个类的名字,即关键字class后的名字)
Polymorphism.cpp----文件名
这里,利用开发者管理工具分别查看了Animal类和Dog类(蓝色下划线的Polymorphism.cpp是文件名)
vfptr ---- v:virtual f----function ptr----pointer, 即虚函数指针
vftable ---- v:virtual f----function ptr----pointer, 即虚函数表
每个类都有一个虚函数指针,指向一个虚函数表,Animal指向的是&Animal::Speak() ,而Dog指向的是&Dog::Speak,
当用父类的引用指向一个子类的对象时,本例中是(Animal &animal = myDog),其本质还是一个子类的对象,调用animal.Speak()后,其实就是调用&Dog::Speak,而非&Animal::Speak()。
也正是上述蓝色字体的描述,导致了动态多态,这样我们就要可以为每种动物,小鸡,小鸭,小猫……设定不同的叫声
如果去掉代码中virtual关键字(即图1),没有发生多态的原因也可以用以上命令查看:
这里无论是Animal还是Dog的size都变成1了,不会再有虚函数指针和虚函数表的概念了,而函数本身又不占类的size,是单独存储的,自然不会发生多态