cpp_virtual - ShenYj/ShenYj.github.io GitHub Wiki
C++中的多态通过虚函数(virtual function)来实现: 多态:虚函数
-
示例代码
struct Animal { /// 未实现多态 void speak() { cout << "Animal::speak()" << endl; } /// 实现多态 virtual void run() { cout << "Animal::run()" << endl; } }; struct Cat : Animal { override void speak() { cout << "Cat::speak()" << endl; } override void run() { cout << "Cat::run()" << endl; } }; void liu(Animal *p) { p->speak(); p->run(); } int main() { liu(new Cat()); getchar(); return 0; }
-
汇编层面区别
/// 非虚函数 p ->speak(); call Animal::speak (函数地址) /// 直接 call 机器码一般 E8 开头 /// 虚函数 p->run(); call eax /// 间接 call (调用寄存器中存放的函数地址) 机器码一般 FF 开头
虚函数的实现原理是虚表,这个虚表里面存储着最终需要调用的虚函数地址,这个虚表也叫虚函数表
使用 sizeof
查看实例对象内存大小会发现实现虚函数后,内存占用会变大 (多一个指针大小空间, 对象的最前面的空间就是存放虚表地址的)
-
虚表汇编分析
函数地址和对象绑定,解决了编译期无法明确类型的问题
-
不同类型有各自的虚表
-
所有的 Cat 对象(不管在全局区、栈、堆)共用同一份虚表
-
如果子类只重写了某一个虚函数,那么虚表中存放着重写后的虚函数地址,同时包括父类虚函数的地址
-
虚表(x86环境的图)
创建对象的时候,就已经将对象可能调用到的函数地址(这里仅针对虚函数)存入到虚表内了,没被重写的就直接调用父类的,遵从继承原则(一步到位,不会先找子类、再找父类 OC 的那一套规则)
-
-
一个虚函数都没有,就不会有虚表
-
父类有一个虚函数,派生类重写就是虚函数,重写时 virtual 可省略
-
当重写虚函数后需要调用父类的成员函数时直接:
父类类名:: 函数名()