基类指针为何能在虚函数中访问派生类成员?内存布局与this指针解析
关于C++多态中this指针与派生类内存布局的疑问解答
首先先把你给出的代码贴出来方便参考:
class Base { public: virtual void test() {}; virtual int get() {return 123;} private: int bob = 0; }; class Derived: public Base{ public: virtual void test() { alex++; } virtual int get() { return alex;} private: int alex = 0; }; Base* b = new Derived(); b->test();
核心结论:你的理解完全正确!
你提到的「派生类内存布局中包含与基类对象完全一致的子布局」,这正是this指针能同时适配基类指针和派生类成员函数调用的关键原因。
咱们一步步拆解:
- 当你用
Base* b = new Derived()创建对象时,Derived对象的内存布局开头部分,就是一个完整的Base子对象——它的结构和单独创建的Base对象完全相同,包含基类的虚表指针、私有成员bob。在这之后,才是派生类自己的成员alex。 - 当调用
b->test()时,隐式传入的this指针,指向的是Derived对象中Base子对象的起始地址。因为这个子布局和独立Base对象完全一致,所以这个this指针完全可以被当作Base*来正常使用。 - 但同时,这个
Base子对象里的虚表指针,实际指向的是Derived类的虚表(这是多态的核心),所以最终会跳转到Derived::test方法执行。
补充细节:派生类成员函数如何通过基类指针访问自己的成员?
可能你会好奇:Derived::test里访问alex时,this明明是Base*类型,怎么能找到alex?
其实编译器在编译Derived的成员函数时,已经提前计算好了alex相对于Derived对象起始地址的偏移量。当this传入时,虽然它的静态类型是Base*,但实际指向的是Derived对象的Base子对象起始地址——编译器会自动把this指针调整为Derived*类型(加上Base类的大小偏移),然后再去访问alex,这个过程是完全透明的。
注意例外情况
上面的逻辑针对的是普通非虚继承的场景,如果你用了虚继承(比如class Derived: virtual public Base),内存布局会有额外的调整(比如虚基类表),但核心思路还是类似的——编译器会自动处理this指针的偏移,保证成员访问的正确性。
内容的提问来源于stack exchange,提问作者Jerry
相关产品推荐
相关产品推荐

