经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
C++中虚表是什么
来源:cnblogs  作者:江水为竭  时间:2024/3/19 9:43:20  对本文有异议

虚函数表,以及虚函数指针是实现多态性(Polymorphism)的关键机制。多态性允许我们通过基类的指针或引用来调用派生类的函数

定义

虚函数(Virtual Function)

  • 定义:类中使用virtual 关键字修饰的函数 叫做虚函数

  • 语法

  1. class Base {
  2. public:
  3. virtual void show() { cout << "Base show" << endl; }
  4. };

虚函数表(Virtual Function Table)

  • 定义:当类含有至少一个虚函数时,编译器会为该类创建一个虚函数表。这个表是一个编译时构建的静态数组,存储了指向类中所有虚函数的指针。如果一个派生类重写了这些函数,那么在派生类的虚表中,相应函数的指针会被更新为指向派生类中的版本。
  • 作用v-table使得在运行时可以实现函数的动态绑定,允许通过基类的指针或引用调用正确的函数版本。

虚函数指针(Virtual Pointer)

  • 定义:每个含有虚函数的类的对象(实例化出的)会持有一个指向相应虚表的指针,这个指针通常被称为虚指针(vptr)。vptr是对象运行时的一部分,确保了当通过基类指针调用虚函数时,能够查找到正确的函数实现。
  • 作用:在对象的生命周期开始时,构造函数会设置vptr以指向相应的虚函数表。如果有派生类对象,它的构造函数会更新vptr,以指向派生类的虚函数表。这保证了通过基类的引用或指针调用虚函数也会执行最派生类的重写版本。

示例

  1. #include <iostream>
  2. using namespace std;
  3. class Base {
  4. public:
  5. virtual void func1() { cout << "Base::func1" << endl; }
  6. virtual void func2() { cout << "Base::func2" << endl; }
  7. };
  8. class Derived : public Base {
  9. public:
  10. void func1() override { cout << "Derived::func1" << endl; }
  11. // func2() 继承自 Base
  12. };
  13. void printVTable(void* obj) {
  14. cout << "vptr Address: " << obj << endl;
  15. void** vTable = *(void***)obj;
  16. cout << "VTable[0] (func1): " << vTable[0] << endl;
  17. cout << "VTable[1] (func2): " << vTable[1] << endl;
  18. }
  19. int main() {
  20. Base* base = new Base();
  21. Derived* derived = new Derived();
  22. cout << "Base object:" << endl;
  23. printVTable(base);
  24. cout << "\nDerived object:" << endl;
  25. printVTable(derived);
  26. delete base;
  27. delete derived;
  28. return 0;
  29. }

程序输出如下,可以看到没用重写的func2函数地址是一样的。

  1. Base object:
  2. vptr Address: 0x8c1510
  3. VTable[0] (func1): 0x422270
  4. VTable[1] (func2): 0x4222b0
  5. Derived object:
  6. vptr Address: 0x8c1530
  7. VTable[0] (func1): 0x422330
  8. VTable[1] (func2): 0x4222b0

如下图所示:

面试题

(来自2024腾讯实习面试)场景题:一个类 A,里面有一个打印 helloworld 的虚函数,然后类 A 会在构造函数里调用这个虚函数,此时有个类 B,继承A,重写了这个 helloworld虚函数,问你在创建类 B 时,会打印 A 里的 helloworld 还是 B 里的。

代码如下:

  1. class A {
  2. public:
  3. A() {
  4. print();
  5. }
  6. virtual void print() {
  7. cout << "A print" << endl;
  8. }
  9. };
  10. class B : public A {
  11. public:
  12. void print() override {
  13. cout << "B print" << endl;
  14. }
  15. };
  16. int main() {
  17. A* aTemp = new B();
  18. delete aTemp;
  19. }

解答:基类构造函数执行的时候,派生类的部分尚未初始化,因此调用的虚函数不会下发到派生类中。

最终会打印 A print,而不是类 B 里重写的版本

原文链接:https://www.cnblogs.com/Az1r/p/18081756

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号