欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > 【C++】普通虚函数 vs 虚析构函数在 vtable 中的绑定方式

【C++】普通虚函数 vs 虚析构函数在 vtable 中的绑定方式

2025/11/10 2:00:05 来源:https://blog.csdn.net/qq_31638535/article/details/146501902  浏览:    关键词:【C++】普通虚函数 vs 虚析构函数在 vtable 中的绑定方式

这篇文章可以回答两个问题: 1. 为什么析构函数需要是虚函数 2. 普通的虚函数和虚析构函数的在虚函数表的绑定方式的区别。

1. vtable(虚函数表)的基本概念

在 C++ 中,每个含有虚函数的类都有一个虚函数表(vtable),这个表存储了指向该类的虚函数实现的指针。当一个对象调用虚函数时,编译器通过 vtable 进行动态绑定

基类 指针指向 派生类 对象时,这里会发生动态绑定

  • 如果调用普通的虚函数,vtable 负责找到 派生类 重写的版本。
  • 如果调用析构函数,vtable 负责确保派生类的析构函数正确执行(如果是虚析构函数的话)。

2. 普通虚函数的 vtable 结构

对于普通的虚函数,我们来看如下代码:

#include <iostream>class Base {
public:virtual void func() { std::cout << "Base::func()" << std::endl; }virtual ~Base() {}
};class Derived : public Base {
public:void func() override { std::cout << "Derived::func()" << std::endl; }
};int main() {Base* obj = new Derived();obj->func(); // 调用 Derived::func()delete obj;  // 调用 Derived::~Derived() -> Base::~Base()
}

普通虚函数的 vtable 绑定情况

如果 Basefunc()Derived 进行了重写,则它们的 vtable 结构如下:

(1) Base 类的 vtable
偏移量vtable 指向的函数
0Base::func()
8Base::~Base()
(2) Derived 类的 vtable
偏移量vtable 指向的函数
0Derived::func()
8Derived::~Derived()

图示:

Base vtable:                      Derived vtable:
+-----------------+                +-------------------+
|  Base::func()  | -----> 重写 ----> |  Derived::func()  |
+-----------------+                +-------------------+
|  Base::~Base()  | -----> 覆盖 ----> |  Derived::~Derived()  |
+-----------------+                +-------------------+
  • Base 类的 func()Derived 重写,因此 Derived 的 vtable 会存放 Derived::func()
  • Base* obj = new Derived(); 并调用 obj->func(); 时,vtable 负责找到 Derived::func(),实现动态绑定。

3. 虚析构函数的 vtable 结构

如果 Base 类的析构函数是虚析构函数,析构调用会发生以下变化:

class Base {
public:virtual ~Base() { std::cout << "Base Destructor" << std::endl; }
};class Derived : public Base {
public:~Derived() override { std::cout << "Derived Destructor" << std::endl; }
};int main() {Base* obj = new Derived();delete obj;
}

运行结果:

Derived Destructor
Base Destructor
  • 先调用 Derived::~Derived(),再调用 Base::~Base()
  • 这是因为 delete obj; 触发了 vtable 查找析构函数,并且 虚析构函数的调用顺序是先派生类,再基类

虚析构函数的 vtable 绑定情况

如果 Base 类的析构函数是 virtual,vtable 会存放派生类的析构函数,并确保 delete obj;先调用派生类析构函数,再调用基类析构函数

(1) Base 类的 vtable
偏移量vtable 指向的函数
0Base::~Base()
(2) Derived 类的 vtable
偏移量vtable 指向的函数
0Derived::~Derived()

图示:

Base vtable:                      Derived vtable:
+-----------------+                +---------------------+
|  Base::~Base()  | -----> 覆盖 ----> |  Derived::~Derived()  |
+-----------------+                +---------------------+|  Base::~Base()      | (继续调用)+---------------------+

4. 关键区别总结

特性普通虚函数虚析构函数
vtable 存储仅存虚函数地址主要存析构函数地址
调用机制Base* 指针指向 Derived*,调用 Derived::func()delete Base* 时,先 Derived::~Derived(),再 Base::~Base()
调用次数只调用 Derived::func()先调用 Derived::~Derived(),再 Base::~Base()
若析构函数非虚delete Base* 只会调用 Base::~Base(),不会调用 Derived::~Derived(),可能导致内存泄漏-

5. 结论

  1. 普通虚函数 在 vtable 中存储的是派生类的函数地址,实现动态绑定。
  2. 虚析构函数 在 vtable 中存储的是派生类析构函数地址,并且保证销毁时先调用派生类析构,再调用基类析构
  3. 析构函数必须是虚的,否则 delete 基类指针时只会调用基类析构函数,导致派生类的资源未释放,造成内存泄漏。
  4. vtable 的结构保证了即使基类指针指向派生类对象,仍然能够正确调用派生类的函数。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词