使用函数指针调用C++虚函数
基本概念:
1. 函数指针,一个地址指针变量,其值指向代码区的某个函数首地址。
2. 虚函数,可以被子类覆写的C++成员函数。由虚函数表实现。
3. 虚函数表指针(vpt),指向虚函数表首地址的指针,一般放在类实例的首4字节(x86系统)。通过它能找的虚函数表,进而能找的某个虚函数。
函数调用:
1. C++函数的普通调用方式:
class Person { private: int value; public: Person():value(100){} virtual int getValue(int a) { return value + a; } }; void main() { Person p; int result = p.getValue(30); // result 为 130 }
2. 使用普通函数指针调用:
class Person { private: int value; public: Person():value(100){} virtual int getValue(int a) { return value + a; } }; typedef int (Person::PFUNC) (int); // 定义函数指针类型 void main() { PFUNC pf = &Person::getValue; // 定义变量 Person p; // int result = p.getValue(30); int result = p.*pf(30); // result 为 130 result = &p->*pf(30); // 语法与上句一样 }
3. 使用函数指针外部调用
class Person { private: int value; public: Person():value(100){} virtual int getValue(int a) { return value + a; } }; typedef int (PFUNC) (int); // 定义函数指针类型 void main() { Person p, *pp; pp = &p; PFUNC pf = (PFUNC)**(int **)pp; // 通过虚函数表获得虚函数pf int result = pf(30); // 崩了,因为没有this指针 } /*以下是老师提供的代码,但是也崩了*/ void main() { Person p, *pp; pp = &p; PFUNC pf = (PFUNC)**(int **)pp; // 通过虚函数表获得虚函数pf __asm mov ecx, pp // 传入this指针 int result = pf(30); // 还是崩了 }
对于第三种方式,this指针传不进去,然后一直报错:
The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
如果我把getValue函数换成无参的,两种方法都可以调用。但是结果第一种是随机数,第二种是100。可见第二种通过汇编方式确实传进去了this指针,(在有多继承时这个this指针一般会做调整的),第一种随机数的原因是它的this指针是类模板装载入内存时的模板首地址,显然它没有初始化。
回到上题,我还在纠结于怎么传参!