虚函数表
静态联编在编译时确定要调用的函数,直接将函数地址编入程序
动态联编运行时观察对象的内存决定具体调用函数。
多态是通过虚函数表实现动态联编
虚对象的第一个内存成员是就是虚函数表指针vptr。

对象实例都指向同一个虚函数表。虚函数表由编译器自动维护,且各类唯一

如果一个类本类有虚函数,其会生成一个虚函数表。
如果一个类的父类或自己有虚函数,会生成虚函数表指针。
虚函数表跟随虚函数。虚函数指针跟随继承。
vptr指针在基类构造是先指向基类虚函数表,子类如果也有虚函数则之后再指向子类的虚函数表,子类的虚函数表会拷贝父类的虚函数表,然后添加自己的虚函数

要添加或重写,就有自己的虚函数表
**如果自己有(虚函数表),就把父类的一起放入,然后修改。
自己没有(虚函数表),就直接指向父类的(虚函数表)**
如何证明 vptr指针的存在
用sizeof计算Parent的大小,发现是4字节,说明类里面有个隐藏的vptr指针
64位机是8字节
sizeof(Parent)
类可以定义为空类,空类大小为1字节,c++规定,每个对象都要有独立的内存地址
#include <iostream>
using namespace std;
class Animal {
public:
// 虚函数,编译器会自动添加虚函数表指针vptr
virtual void cry(){
cout << "cry" << endl;
}
virtual void laugh(){
cout << "laugh" << endl;
}
};
// 在 C++ 中,类(Class)本身不占用内存,只有类的实例(对象)才会占用内存。
int main(int, char**){
/*
sizeof(Animal) 并不是计算类本身的内存占用,而是计算 **Animal 类型的对象在内存中占用的字节数**。
即使没有创建实例,sizeof(Animal) 仍然会返回 “如果创建一个 Animal 对象,它会占用多少内存”。
*/
cout << sizeof(Animal) << endl;
Animal a;
//((int*)&a); // 强转,把对象内存空间看作一个数组
// ((int*)&a)[0] 拿到第一个元素,vptr
typedef void (*pf)();
// *((int*) (((int*)&a)[0])) // 拿到表中第一个元素
((pf)*((int64_t*) (((int64_t*)&a)[0])))();
}
this参数的作用
多态
访问其他的本类成员
所以强转后不访问类成员与绕过检查是可以直接运行函数的
为什么强转可以不用传入this
在 C++ 中,非静态成员函数的调用通常依赖于 this 指针,它指向当前对象实例。但当通过强制类型转换(如将成员函数指针强转为普通函数指针)后,不传入 this 也能调用,本质原因是编译器对成员函数的底层处理机制和强转绕过了类型检查:
- 成员函数的底层实现
非静态成员函数在编译后,会被处理为类似“带有额外参数”的普通函数,这个额外参数就是 this 指针(位于参数列表的第一个位置)。例如:
class A {
public:
void func(int x) { ... }
};
编译后会被处理为类似:
void A_func(A* this, int x) { ... }
调用 a.func(5) 时,编译器会自动传入 &a 作为 this 指针。
- 强制类型转换的影响
当将成员函数指针强转为普通函数指针(如 void(*)(int) )时,相当于告诉编译器:“忽略 this 指针参数,按普通函数的参数列表处理”。
此时调用该函数时,编译器不会自动传入 this 指针,参数列表也会被强行匹配(例如原本需要 this + int ,强转后只需要 int )。
- 为什么能调用但危险?
- 能调用:因为函数的机器码已经存在,强转后只是绕过了编译器的类型检查,直接跳转到函数地址执行。
- 危险:由于缺少 this 指针,函数内部如果访问成员变量或其他成员函数,会因 this 指针无效(可能是随机值)导致未定义行为(如崩溃、数据错乱)。
总结
强转后不传入 this 能调用,是因为绕过了编译器对 this 指针的检查和传递机制,但这种行为是未定义且危险的,实际开发中绝对禁止使用。

浙公网安备 33010602011771号