概念:
当父类型的指针或者引用指向子类对象时,通过父类型的指针调用虚函数,如果子类重写了这个虚函数,则调用的表现是子类的,否则就是父类型的。
注意:如果调用的是名字隐藏的函数,则调用的是父类的函数,而不是子类的函数。因为指针类型是父类型的
继承是构成多态的基础。
虚函数是构成多态的关键。
函数重写是必备条件
举个通俗易懂的例子:
#include <iostream>
using namespace std;
class Animal{
public:
void show(){
cout << "animal show()" << endl;
}
virtual void run(){
cout << "animal run()" << endl;
}
virtual void fun(){
cout << "animal fun()" << endl;
}
};
class Cat:public Animal{
public:
void show(){
cout << "cat show()" << endl;
}
virtual void run() { //是否带virtual关键字都重写了父类的虚函数
cout << "cat run()" << endl;
}
void fun(){ //是否带virtual关键字都重写了父类的虚函数
cout << "cat fun()" << endl;
}
};
class Fish:public Animal{
public:
void run(){
cout << "fish run()" << endl;
}
void fun(){
cout << "fish fun()" << endl;
}
};
int main(){
cout << "子类对象调用子类函数" << endl;
Cat cat;
cat.show();
cat.run();
cat.fun();
cout << "子父类对象调用父类函数" << endl;
Animal animal;
animal.show();
animal.run();
animal.fun();
cout << "父类型指针指向子类对象,用到多态,需要注意子类对父类的虚函数重写" << endl;
Animal *pa = &cat;
pa->show(); //animal
pa->run(); //cat
pa->fun(); //cat
cout << "拷贝构造函数,注意不涉及多态,直接调用父类函数" << endl;
Fish fish;
Animal af = fish; //调用animal的拷贝构造函数,本质是定义初始化一个af对象
af.show(); //animal
af.run(); //animal
af.fun(); //animal
cout << "父类型引用指向子类对象,用到多态,需要注意子类对父类的虚函数重写" << endl;
Animal& an = fish;
an.show(); //animal
an.run(); //fish
an.fun(); //fish
}
运行结果:
Animal是父类,其含有run()和fun()两个虚函数, 另一个是普通的成员函数show()。 子类Cat和Fish都继承了父类Animal并都重写了run()和fun()函数。所以当Animal类型的指针或者引用指向子类对象的时候,调用run()和fun()函数,最终表现为调用子类重写的run()和fun()函数。
因为show()函数在父类中只是普通的成员函数,那么子类中的show()函数不构成对父类show()的重写。因此,父类型的指针调用show()还是调用父类的show()。
应用:
既想调cat的run(),又想调用fish的run(),那么一般只能分别调用两次(cat.run(); fish.run())。 如果有多个子类都要调用run(),那么上述调用方法就显得有些累赘。 因此有一个较好的方法,就是实现一个函数,参数是父类型的引用, 在函数体中使用父类型的引用调用父类型的虚函数。 然后调用该函数时,传入子类对象, 就可以通过多态来实现一个函数灵活调用多个子类重写父类的函数。
举例: 在上述代码的基础上:
void run(Animal& animal) {
animal.run();
}
main函数调用:
run(cat);
run(fish);
结果: