探究vptr字段及C++类中vptr、vtable工作机制的技术问询
聊聊vptr、vtable的工作机制,以及栈堆状态那点事
嘿,先默认你是在类似这样的C++代码场景下提问(毕竟你没贴具体代码,但结合描述我大概能猜个八九不离十):
class A { public: virtual void do_something() {} }; class B : public A { public: void do_something() override {} // 不管重没重写,咱们都覆盖到 }; int main() { B stack_b; // 栈上的B对象 A* heap_a_ptr = new B(); // 堆上的B对象,用基类指针指向 // 或者你可能是new A()?但结合你的描述更偏向前者 }
1. 两个vptr的值到底一样不?
这核心要看堆上的对象实际类型是什么:
- 如果堆上的对象是
B类型(就像上面代码里的new B(),哪怕用A*指针指着它):那栈上stack_b里的vptr,和堆上那个B对象里的vptr,绝对指向同一个地址——也就是B类的vtable地址。因为同一个类的所有对象,都是共享同一个vtable的,它们的vptr自然都指向这个唯一的全局vtable。 - 如果堆上的对象是纯纯的
A类型(比如new A()):那堆上对象的vptr指向A的vtable,栈上B对象的vptr指向B的vtable,这时候两者的值肯定不一样。
你提到“堆上的A::vpointer”,可能是误以为基类指针指向的对象,它的vptr就属于A?其实不是的,vptr是跟着对象走的,对象实际是什么类型,vptr就指向那个类的vtable,基类指针只是个“视角”,改不了对象本身的类型和vptr的指向。
2. 到底有多少个vtable?
这得看你的类结构:
- 如果
A是带虚函数的基类,B是它的子类:- 要是
B重写了A的虚函数,那A和B各有一个独立的vtable,总共2个。 - 要是
B没重写任何A的虚函数,有些编译器可能会搞点优化,让B复用A的vtable,但从C++标准的角度来说,每个多态类(带虚函数或者继承自多态类)都该有自己的vtable。实际开发里,大部分编译器哪怕这种情况还是会生成独立的vtable,除非做了极端优化。
- 要是
- 要是你还有其他继承层级的多态类,那每个类都会对应一个专属的vtable。
额外补一句:栈和堆的状态细节
- 栈上的B对象:存在当前函数的栈帧里,包含
B的所有成员变量,还有一个vptr(通常是对象的第一个成员,具体位置由编译器说了算),这个vptr死死指着B的vtable。 - 堆上的对象:如果是
new B(),那堆上会分配一块内存,存着B的所有成员(包括从A继承来的部分)和vptr,vptr同样指向B的vtable;如果是new A(),那堆上就是纯A的对象,vptr指向A的vtable。 - 对了,vtable本身并不在栈或者堆里,它是编译期就生成的,存在程序的**只读数据段(.rodata)**里,属于全局共享的结构,同一个类的所有对象都共用这一个vtable。
内容的提问来源于stack exchange,提问作者Adam Morad
相关产品推荐
相关产品推荐

