画出两次间接寻址的示意图。
当我们需要访问一个内存地址中存储的数据时,如果这个地址本身存储的是另一个地址,那么就需要进行两次间接寻址。下面是两次间接寻址的示意图:
+---+ +---+ +----+
a | | ---> | | ---> | 42 |
+---+ +---+ +----+
↑
|
+---+ +---+
b | | ---> | |
+---+ +---+
↑
|
+---+
c | |
+---+
在上面的示意图中,变量 a
存储的是一个指向另一个地址的指针,而这个地址中存储的又是另一个地址,最终指向存储实际数据的地址。通过两次间接寻址,我们可以得到存储在最终地址中的数据,即 42
。
c++中利用虚函数实现动态绑定的原理图
C++ 中通过虚函数实现动态绑定的原理
1. 虚函数的作用及其必要性
虚函数的主要作用是支持运行时多态。在面向对象编程中,基类指针或引用可以指向派生类的对象。如果没有虚函数机制,在这种情况下调用方法时,默认会执行基类的方法版本。而引入虚函数后,则可以通过基类指针或引用调用派生类的具体实现[^1]。
2. 动态绑定的核心概念
动态绑定是指程序在运行时而非编译时决定应该调用哪个具体函数的过程。这一过程依赖于 虚函数表 (vtable) 和 虚表指针 (vptr) 的存在。当一个类声明了一个虚函数时,编译器会在后台为这个类创建一张虚函数表,并让每个类实例都持有一个指向这张表的指针[^4]。
3. 虚函数表的工作方式
- 每个含有虚函数的类都有自己的虚函数表。
- 类的所有虚函数地址会被存储在这张表中。
- 对象内部维护一个隐藏的指针(即 vptr),用于指向所属类的虚函数表[^2]。
以下是虚函数表和虚表指针工作机制的一个简化描述:
假设我们有如下代码结构:
class Base {
public:
virtual void f() { std::cout << "Base::f()\n"; }
};
class Derived : public Base {
public:
void f() override { std::cout << "Derived::f()\n"; }
};
对于上述代码,Base
和 Derived
各自拥有独立的虚函数表。Base
的虚函数表包含 Base::f()
函数地址;而 Derived
的虚函数表则覆盖了父类条目并替换成自身的 Derived::f()
地址[^3]。
4. 动态绑定的实际流程
假设有以下场景:
Base* ptr = new Derived();
ptr->f(); // 输出什么?
这里的关键在于如何解析 ptr->f()
的行为:
- 编译期:由于
ptr
是Base*
类型,因此查找的是Base
定义下的虚函数签名。 - 运行期:访问
ptr
所指向对象的真实类型 (Derived
) 并利用其对应的虚函数表找到最终要调用的目标函数Derived::f()
。
5. 原理图解释
下图为典型的虚函数表与动态绑定工作原理示意:
此图展示了两个重要组件之间的关系:
- 虚表指针: 指向当前对象关联的虚函数表。
- 虚函数表: 列出了对应类所有的虚函数入口地址。
每当通过基类指针或者引用调用虚函数时,实际上经历了一次间接寻址操作 —— 即先定位到目标对象的虚表指针位置,再依据索引获取相应函数地址完成跳转。
相关推荐












