虚函数
被Virutal
关键字修饰的成员函数为虚函数,虚函数的作用是实现多态。多态是将接口与实现分开的技术。
虚函数表
虚函数表位于类的起始地址,通过虚函数表可实现函数的间接调用,若子类需要重写父类的方法,则将虚函数表内的函数地址修改即可。通过this指针可以检索到虚函数表的位置。
无this指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include "stdio.h" class A { private: virtual void AF() { printf("AF run...\n"); } }; int main() { A a; typedef void(*Myfunction)(); Myfunction f = (Myfunction)(*(int*)(*(int*)(&a))); f(); } 运行结果:AF run...
|
类初始化之前,会将需要初始化的类地址传递给ecx
寄存器。实际该值为this
指针。
经过构造器之后,该地址值会被填充为虚函数地址表。
那么想要通过虚函数表调用类函数则需要先取出类地址->取出虚函数表地址->取出函数地址
修复this指针
(1) 即使没有显示的调用this,在汇编中也会隐式的传递this指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include "stdio.h" class A { public: int a = 1; private: virtual void AF() { printf("AF run...\n"); printf("a=%d\n", this->a); } public: virtual void test(int a) { printf("test run...%x\n", a); } }; int main() { A a; a.test(0x12345678); }
|
在使用类变量引用函数时,无需间接调用函数,而是直接调用。
(2)通过传递this
指针输出变量a
的值,可以看到在AF函数中输出变量的a
的值依靠this
指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include "stdio.h" class A { public: int a = 1; private: virtual void AF() { printf("AF run...\n"); printf("a=%d\n", this->a); } }; int main() { A a; A* pA = &a; typedef void(*Myfunction)(); Myfunction f = (Myfunction)(*(int*)(*(int*)(&a))); __asm { mov ecx, pA; } f(); }
|
若不显示传递this
指针,则没办法定位的a
的位置
(3)传递任意class的this给调用的虚函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include "stdio.h" class A { public: int a = 1; private: virtual void AF() { printf("AF run...\n"); printf("a=%d\n", this->a); } }; class B: public A { public: int a = 2; private: virtual void AF() { printf("BF run...\n"); printf("a=%d\n", this->a); } }; int main() { A a; B b; typedef void(*Myfunction)(); Myfunction f = (Myfunction)(*(int*)(*(int*)(&a))); B* pA = &b; __asm { mov ecx, pA; add ecx, 4; } f(); } 运行结果: AF run... a=2
|
要想要获取B类中的a的值,就需要获取B类的this指针。
这里需要注意的是,不能直接将B类的地址作为this
指针传入,这样获取的类A的this
指针,这是因为类B是继承类A的,在内存中,会首先存储类A的结构,接着才是类B的。因此B类的this
指针实际是指向类A。
虽然类B是继承类A,但是类B与类A的虚函数地址是不一致的,那么多态的实现实际是修改了虚函数地址表中函数的地址。
若类B继承类A,但是没有重写方法。虚函数地址表会改变。如下图
函数地址未改变,因此若是多态的话,这函数地址会改变。
参考链接
https://bbs.kanxue.com/thread-278697.htm%EF%BC%89