多态分为静态多态和动态多态,但是我们所指的多态一般都是动态多态
静态多态体现在函数重载和运算符重载
动态多态体现在派生类和虚函数运行时多态
静态多态和动态多态的区别在于静态多态的函数和动态多态的函数地址绑定时间不同,
静态多态函数地址在编译时就已经早绑定,动态多态函数的地址在运行时才绑定,所以动态多态的函数实现更为灵活。
下面是动态多态的使用例子
动物类
class Animal
{
public:
virtual void Speak()
{
cout << "动物在说话" << endl;
}
};
//猫类
class Cat : public Animal
{
public:
//重写
//函数返回值类型 函数名 参数列表 完全相同 子类的virtual可写可不写
virtual void Speak()
{
cout << "小猫在说话" << endl;
}
};
//狗类
class Dog :public Animal
{
public:
void Speak()
{
cout << "小狗在说话" << endl;
}
};
void DoSpeak(Animal &animal) // Animal &animal = cat C++中允许父子之间的类型转换 并且不需要做强制类型转换 父类的指针或者引用可以直接指向子类对象
{
animal.Speak();
}
void Test1()
{
Cat cat;
DoSpeak(cat);
Dog dog;
DoSpeak(dog);
}
调用Test1函数,会分别输出小猫在说话和小狗在说话,这就是动态多态的好处,地址晚绑定,可以更灵活的输出自己想要的结果。
但是为什么会有这样的功能呢?
我们可以利用开发人员命令框来看一下这三个类的底层框架
Animal类:
可以看出Animal类size的大小为4个字节,如果Speak函数没有设置为虚函数,我们可以得知,Animal类的size大小应该为1个字节,那么现在为4个字节是因为在Animal类中存储了一个vfptr,也就是虚函数(表)指针,这个指针指向了vftable(虚函数表),这个表中存放着&Animal::Speak,Animal类中Speak函数的地址。
Cat类:
Cat类中继承了基类Animal类中的vfptr,如果我们没有重写Cat中的虚函数,那么他会继承基类的vfptr,所以我们之后调用的时候就会调用父类中的Speak函数,但是重写了虚函数后,vftable中将父类的Speak函数地址修改为Cat类的Speka函数地址,所以我们之后调用时就调用了Cat中的Speak函数,Dog类同理。所以动态多态实现了地址的晚绑定,可以更加灵活的调用我们的函数。