我们先来看一段代码,进行debug问题分析。
class Teacher22 {
public:
void virtual func1() {
cout << "Teacher22 vir func1 called this = " << this << endl;
}
void virtual func2() {
cout << "Teacher22 vir func2 called this = " << this << endl;
}
};
void main() {
//我们打印一下 func1 和func2的地址
printf("Teacher22::func1 的地址是%p\n",&Teacher22::func1);
printf("Teacher22::func2 的地址是%p\n", &Teacher22::func2);
Teacher22 *ptea = new Teacher22();
cout << "断点在这里" << endl;
}
问题:debug 和 打log发现虚函数的地址不一样。
打log
Teacher22::func1 的地址是00B5121C
Teacher22::func2 的地址是00B515FF
在程序执行中debug
[0] = 0x00b51802 {ConsoleApplication2.exe!Teacher22::func1(void)}
[1] = 0x00b517fd {ConsoleApplication2.exe!Teacher22::func2(void)}
这两个不一样呀,那个是真的呢?
再次验证,在内存中 查找ptea的值,
Teacher22 *ptea = new Teacher22();
前四个值就是vptr的值,
64 5e b6 00
0x00b65e64
再次查看内存 中 这个vptr的值
02 18 b5 00 fd 17 b5 00
也就是 00b51802 00b517fd
也就是说:
我们在程序debug中看到的是真实的值,
那么通过 Teacher22::func1打印的这个虚函数地址为什么有问题呢?
原因:
再次查看 反汇编代码,会发现,里面有一个vcall的调用,我们可以将vcall理解为一段代码,
也就是说:在 &Teacher22::func1 中打印的实际上vcall的地址,然后通过vcall的调用真正的调用到虚函数中。
也就是说;通过
printf("Teacher22::func1 的地址是%p\n",&Teacher22::func1);
//实际上上面函数打印的是vcall函数地址,而不是真正的虚函数地址
为什么要有这个vcall?
1.在多重继承的时候,可以调整this指针的偏移。
2.跳转到真正的虚函数中去。