在面向对象中,多态(动态多态)是指基类指针或引用指向子类对象,在运行时根据基类的引用(指针)的指向内容动态调用实际绑定对象函数的行为。
与之相对应的是静态多态,静态多态是编译器在编译期间完成的,编译器会根据实参类型来选择调用合适的函数,如果有合适的函数就调用,没有的话就会发出警告或者报错,如函数重载,非虚函数重写等。
多态是设计模式/软件框架的基础。
当类中声明了虚函数时,编译器就会为该类生成一张虚函数表,且由编译器自己维护,里面存储的是该类的所有虚(virtual)成员函数。
之后当我们用这个类创建对象时,就会在对象中产生一个 vptr指针(虚函数指针),存储在实例对象内存的最前面的位置,这个vptr指针指向的就是该类的虚函数表。
在多态函数调用时, 实例对象会通过vptr指针到该类的虚函数表中查找实际应被调用的函数,从而找到正确的函数入口地址。
注:出于效率考虑,没有必要将所有成员函数都声明为虚函数。
当父子类都含有虚函数时:
1. 构造函数可以成为虚函数吗?
不可以。因为父类对象会在子类之前进行构造,此时子类部分的数据成员还未初始化, 因此调用子类的虚函数是不安全的,故而C++不会进行动态联编。
2. 析构函数可以成为虚函数吗?
可以,并且产生动态多态。因为析构函数是在对象销毁之前被调用,即在对象销毁前 虚函数表指针是正确指向对应的虚函数表。且建议将析构函数声明为虚构函数的,因为虚析构函数可以指引 delete 运算符正确析构动态对象,即当父类指针指向子类对象时,通过父类指针去释放所有子类的内存空间,避免内存泄露。
3. 构造函数中可以调用虚函数发生多态吗?
构造函数中可以调用虚函数,但是不可能发生多态行为,因为在构造函数执行时,虚函数表指针可能未被正确初始化。
4. 析构函数中可以调用虚函数发生多态吗?
析构函数是用来销毁一个对象的,在销毁一个对象时,先调用子类的析构函数,然后再调用基类的析构函数。所以在调用基类的析构函数时,派生类对象的数据成员已经“销毁”,这个时再调用子类的虚函数已经没有意义了。