You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++基类指针调用子类虚函数为何能访问子类成员?

为什么Base*指针调用子类虚函数时能访问子类成员?

我可能陷入了混淆。我知道带虚函数的C++类拥有vtable(每个类类型对应一个vtable),Base类的vtable包含一个元素&Base::print(),Child类的vtable包含一个元素&Child::print()。

当我声明base和child两个对象时,base的vtable_ptr指向Base类的vtable,child的vtable_ptr指向Child类的vtable。将base和child的地址赋值给Base类型指针数组后,我调用base_array[0]->print()和base_array[1]->print()。

我的疑问是:base_array[0]和base_array[1]都是Base类型,运行时虽然vtable查找能得到正确的函数指针,但Base类型为何能访问Child类的成员(即value2)?调用base_array[1]->print()时,base_array[1]是Base类型,但运行时会调用Child类的print(),可我疑惑为何此时能访问value2,毕竟我使用的是Base类型……我肯定漏掉了某些知识点。

附上对应的测试代码:

#include "iostream"
#include <string>
using namespace std;
class Base {
public:
 int value;
 string name;
 Base(int _value, string _name) : value(_value),name(_name) { }
 virtual void print() {
 cout << "name is " << name << " value is " << value << endl;
 }
};
class Child : public Base{
public:
 int value2;
 Child(int _value, string _name, int _value2): Base(_value,_name), value2(_value2) { }
 virtual void print() {
 cout << "name is " << name << " value is " << value << " value2 is " << value2 << endl;
 }
};
int main() {
 Base base = Base(10,"base");
 Child child = Child(11,"child",22);
 Base* base_array[2];
 base_array[0] = &base;
 base_array[1] = &child;
 base_array[0]->print();
 base_array[1]->print();
 return 0;
}

嘿,这个问题正好戳中了C++多态里容易混淆的核心细节——静态类型与动态类型的区别,以及成员函数中this指针的实际类型,咱们一步步理清楚:

  • 首先要明确:base_array[1]的静态类型确实是Base*(编译时编译器看到的类型),但它的动态类型(运行时实际指向的对象类型)是Child*。这是多态的基础:静态类型负责编译时的语法检查,动态类型决定运行时的实际行为。

  • 重点来了:当你通过Base*调用虚函数print()时,运行时会通过vtable找到Child::print(),而调用这个成员函数时,C++会自动把实际对象的完整地址作为this指针传递给函数。也就是说,在Child::print()内部,this的类型是Child*,而不是Base*!

    这就是能访问value2的原因:Child::print()是Child类的成员函数,它本来就有权访问Child的所有成员,而此时this指向的是一个完整的Child对象,自然能直接访问value2。

  • 这里要区分两种完全不同的场景:

    1. 如果你直接通过Base*访问子类成员,比如写base_array[1]->value2,编译器会直接报错——因为静态类型Base*不知道value2的存在,这是编译时的强类型检查。
    2. 但在子类的成员函数内部,this的类型是子类指针,访问子类成员完全合法,这和调用函数时用的是什么类型的指针无关。
  • 再补充个内存布局的视角:Child对象在内存中,先存放Base的部分(包括vtable指针、value、name),后面紧跟着value2。当Base*指向Child对象时,它指向的是Child对象中Base子对象的起始地址,但虚函数调用找到Child::print()后,this指针会被自动调整为指向整个Child对象的起始地址(部分编译器会做这个调整),所以函数里能正确拿到value2。

总结一下:不是Base*能访问子类成员,而是你调用的子类成员函数本身就可以访问自己的成员,而这个函数运行时拿到的this是指向完整子类对象的指针——这就是动态绑定带来的巧妙效果!

内容的提问来源于stack exchange,提问作者Kaiyu Shen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:56:56