非虚函数调用虚函数时vtable与vptr的工作机制及案例解析
C++虚函数表(vtable)与虚指针(vptr)工作机制解析
代码回顾
#include <bits/stdc++.h> using namespace std; class Base { public: virtual void print() { cout << "Base class print function\n"; } void invoke() { cout << "Base class invoke function\n"; this->print(); // case: 1 print(); // case: 2 Base::print(); // case: 3 } }; class Derived: public Base { public: void print() { cout << "Derived class print function\n" ; } void invoke() { cout << "Derived class invoke function\n"; this->print(); } }; int main() { Base *b = new Derived; b->invoke(); return 0; }
核心逻辑拆解
首先明确:Base* b = new Derived; 创建的是Derived类实例,仅用Base类型指针指向它。该实例的内存头部继承自Base类,包含一个vptr(虚指针),这个指针指向的是Derived类的虚函数表(vtable),而非Base类的。
三个调用场景的具体机制
case: 1 this->print()
this是当前对象的指针,静态类型为Base*,但实际指向的是Derived实例,动态类型为Derived*。- 由于
print是虚函数,调用时会通过this指向的对象中的vptr,找到对应的vtable,再调用表中存储的print函数地址。 - 流程:
this(Derived实例) → vptr → Derived的vtable →Derived::print()
case: 2 print()
- 在成员函数中直接调用虚函数,C++编译器会自动将其解析为
this->print(),和case1的执行逻辑完全一致,同样通过vptr和Derived的vtable找到Derived::print()。
case: 3 Base::print()
- 这里使用了作用域限定符
::,明确指定调用Base类的print函数,会直接跳过虚函数的动态绑定机制,直接执行Base类的print实现,完全不涉及vptr和vtable。
内存布局与调用流程示意图(文本版)
Derived 实例内存布局: +-----------------------+ | vptr (指向Derived vtable) | +-----------------------+ | Base类成员(若存在) | +-----------------------+ | Derived类成员(若存在) | +-----------------------+ Derived 虚函数表(vtable): +-----------------------+ | 函数地址: &Derived::print | +-----------------------+ Base 虚函数表(vtable): +-----------------------+ | 函数地址: &Base::print | +-----------------------+ case1/case2 调用流程: b (Base*) → Derived实例 → vptr → Derived vtable → Derived::print() case3 调用流程: 直接跳转至&Base::print的地址执行,不经过vptr/vtable
代码运行输出
执行代码后会得到如下结果,验证上述逻辑:
Base class invoke function Derived class print function Derived class print function Base class print function
内容的提问来源于stack exchange,提问作者Kumar Roshan Mehta
相关产品推荐
相关产品推荐

