一、虚机制的引入
利用动态编联实现——虚函数来解决上述问题
二、虚函数
必须是实例方法,不能是类方法。
- 若基类中析构函数为虚函数,则派生类中的析构函数不论写不写virtual关键字都是虚函数。
- 派生类中新的虚函数应尽量避免与基类中的虚函数重名。
三、虚函数表
1、引入:
2、定义:
- 说明中的第一点,虚函数可以是自己定义的也可以是从基类中继承的
- 说明中的第四点,派生类中的前几个,顺序与基类中定义的函数顺序相同,然后才是派生类中自己定义的函数。
- 若在基类的Show()函数前加上virtual关键字,运行结果不变
- vptr虚拟表指针,指向虚拟表。
四、虚机制的作用机制
1、
- sh1静态类型为Shape类型,动态类型为矩形;psh2静态类型为Shape指针类型,动态类型为圆的指针类型。
- Func可能不被调用,因为通过虚拟表找到的未必就是Func函数。
EX1:
- 标红的代码部分,需注意p1、p2的静态类型都是A类,而在A类中是找不到f1()的,因此编译报错。
EX2:
- Date类析构函数加virtual关键字的原因:如果不加virtual关键字则不会采用虚机制,在delete pDate1的时候就不会调用派生类的析构函数,而是直接释放基类的析构函数。
五、虚函数的访问
- 给p2发送的消息X仅限于f、g、k,因为p2的静态类型为A类,而在A类中无h函数。
- 若给p2发送消息f,则找到B类中的f,而B中f大括号内的g()和k()全部视为this->g()/k(),其中this->g()找到A类中,g()中的k()视作this->k(),也是A类中的k(),非虚函数为静态编联。而f中的this->k()在B类中,其中的h()视作this->h(),仍在B类中,非虚函数为静态编联。
示例:
- p->vf()时,输出Derived::vf()。
- p->vg()时,由于派生类中没有vg(),只有基类中有,且基类中为虚函数,所以动态编联,而动态类型为派生类类型,所以其中的this->vf()为派生类中的vf,同样输出Derived::vf()。
- p->nvh()时,由于静态类型Base中nvh()为非虚函数,所以其实现全部在基类中完成。
输出:
Base::nvh()
Base::vf()
- delete p时,由于p是Base类指针,所以先调用Base类的析构函数,而其为虚函数,所以动态编联执行派生类的析构函数,执行vf(),最后再执行基类析构函数中的vf().
输出:
Derived::vf()
Base::vf()