基类指针为何能访问派生类的vptr成员变量?
基类指针为何能访问派生类的vptr?
先看你给出的代码示例:
#include <iostream> class Base { public: virtual void foo() { std::cout << "Base::foo()" << std::endl; } }; class Derived : public Base { public: void foo() override { std::cout << "Derived::foo()" << std::endl; } }; int main() { Base* basePtr = new Derived(); basePtr->foo(); delete basePtr; return 0; }
核心原因可以拆解成这几点:
- vptr是编译器隐式添加的隐藏成员:你代码里没声明vptr,它是编译器为支持多态,给所有带虚函数的类(包括基类和派生类)自动插入的隐藏成员。对于public继承的派生类对象,其内存布局开头是基类子对象,vptr就存放在这个基类子对象里。当创建Derived对象时,编译器会自动把基类子对象里的vptr指向Derived的vtable,而非Base的。
- 基类指针指向的是派生类对象的基类子对象起始地址:
Base* basePtr = new Derived()执行后,basePtr的地址对应Derived对象中Base子对象的起始位置,这个位置里就存着指向Derived vtable的vptr——编译器生成代码时,知道只要是Base类型的指针(Base是多态类),指向的对象里一定有vptr,所以直接从该地址取vptr,不用管实际对象是Base还是Derived。 - 非虚成员函数和vptr的访问逻辑完全不同:非虚成员函数是编译期绑定的,编译器会严格按指针的静态类型(Base*)检查成员是否属于Base类,派生类的非虚成员不在Base定义里,所以编译报错。但vptr的访问是编译器处理虚函数调用时自动完成的运行期行为,不是你显式访问某个类成员,因此不受静态类型的编译期检查限制。
简单说:你以为basePtr在“访问Derived的vptr”,其实它只是在访问自己静态类型(Base)要求的那个隐藏vptr成员,而这个成员在派生类对象里被替换成了指向派生类vtable的版本而已。
内容的提问来源于stack exchange,提问作者H Kumar
相关产品推荐
相关产品推荐

