目录
多态
C++支持编译时多态(静态多态)和运动时多态(动态多态),运算符重载和函数重载就是编译时多态,而派生类和虚函数实现运动时多态。
静态多态和动态多态的区别在于函数地址是早绑定(静态联编)还是晚绑定(动态联编)。如果函数的调用,在编译阶段可以确定函数的调用地址并产生代码,就是静态多态,就是说地址是早绑定的。而如果函数的调用,其地址不能编译不能在编译期间确定,而需要在运行时才能确定,这就是属于晚绑定(动态联邦、运行时多态)。
多态成立条件:
- 继承
- 子类重写父类虚函数(与父类完全一致,子类这种virtual关键字可写可不写,建议写)
无多态问题:早绑定
代码示例如下:其结果为:动物在说话!但是我们给doSpeak传入的对象为cat,而不是animal对象,输出结果应该为小猫在说话。
#define _CRI_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;
class Animal
{
public:
void speak()
{
cout << "动物在说话" << endl;
}
};
class Cat :public Animal
{
public:
void speak()
{
cout << "小猫在说话" << endl;
}
};
//调用doSpeak, speak函数的地址早就绑定好了,早绑定(编译阶段就已确定好了地址),叫静态联编
//如果想调用猫的speak,不能提前绑定好函数的地址,所以需要在运行时去确认函数地址
//动态联编, 写法doSpeak 方法改为虚函数,在父类上申明一个虚函数, 发生了多态
//多态:父类的引用或指针 指向子类对象
void doSpeak(Animal& animal) //Animal& animal =cat
{
animal.speak();
}
//如果发生了继承的关系,编译器允许进行类型转换
void test01()
{
Cat cat;
doSpeak(cat);
}
int main()
{
test01();
return 0;
}
当绑定在程序运行之前(由编译器和连接器)完成时,称为早绑定(静态联编)。因为编译器在只有Animal地址时并不知道要调用的正确函数。编译是根据指向对象的指针或引用的类型来选择函数调用。这个时候由于DoBussiness的参数类型是Animal&,编译器确定了应该调用的speak是Animal::speak的,而不是真正传入的对象cat::speak。
动态绑定
解决此现象的方法为动态绑定(动态联编),实现动态联编的方法为虚函数(virtual function)。C++动态多态性是通过虚函数实现的,虚函数允许子类(派生类)重新定义父类(基类)成员函数,这种方法称为覆盖(重写)。
实现方法为在基类声明此函数时使用virtual关键字。
- 为创建一个需要动态绑定的虚成员函数,可以简单在这个函数声明前面加上virtual关键字。
- 如果一个函数在基类中被声明为virtual,那么在所有派生类中它都是virtual的。
- 在派生类中virtua