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

定义

虚函数(Virtual Function)

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

  • 语法

class Base {
public:
virtual void show() { cout << "Base show" << endl; }
};

虚函数表(Virtual Function Table)

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

虚函数指针(Virtual Pointer)

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

示例

#include <iostream>
using namespace std; class Base {
public:
virtual void func1() { cout << "Base::func1" << endl; }
virtual void func2() { cout << "Base::func2" << endl; }
}; class Derived : public Base {
public:
void func1() override { cout << "Derived::func1" << endl; }
// func2() 继承自 Base
}; void printVTable(void* obj) {
cout << "vptr Address: " << obj << endl;
void** vTable = *(void***)obj;
cout << "VTable[0] (func1): " << vTable[0] << endl;
cout << "VTable[1] (func2): " << vTable[1] << endl;
} int main() {
Base* base = new Base();
Derived* derived = new Derived(); cout << "Base object:" << endl;
printVTable(base); cout << "\nDerived object:" << endl;
printVTable(derived); delete base;
delete derived; return 0;
}

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

Base object:
vptr Address: 0x8c1510
VTable[0] (func1): 0x422270
VTable[1] (func2): 0x4222b0 Derived object:
vptr Address: 0x8c1530
VTable[0] (func1): 0x422330
VTable[1] (func2): 0x4222b0

如下图所示:

面试题

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

代码如下:

class A {
public:
A() {
print();
}
virtual void print() {
cout << "A print" << endl;
}
}; class B : public A {
public:
void print() override {
cout << "B print" << endl;
}
};
int main() {
A* aTemp = new B();
delete aTemp;
}

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

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

C++中虚表是什么的更多相关文章

  1. 揭开C++类中虚表的“神秘面纱”

    C++类中的虚表结构是C++对象模型中一个重要的知识点,这里咱们就来深入分析下虚表的在内存中的结构. C++一个类中有虚函数的话就会有一个虚表指针,其指向对应的虚表,一般一个类只会有一个虚表,每个虚表 ...

  2. C++反汇编第三讲,反汇编中识别虚表指针,以及指向的虚函数地址

    C++反汇编第三讲,反汇编中识别虚表指针,以及指向的虚函数地址 讲解之前,了解下什么是虚函数,什么是虚表指针,了解下语法,(也算复习了) 开发知识为了不码字了,找了一篇介绍比较好的,这里我扣过来了,当 ...

  3. C++反汇编第二讲,反汇编中识别虚表指针,以及指向的虚函数地址

    讲解之前,了解下什么是虚函数,什么是虚表指针,了解下语法,(也算复习了) 开发知识为了不码字了,找了一篇介绍比较好的,这里我扣过来了,当然也可以看原博客链接: http://blog.csdn.net ...

  4. C++中的初始化

    C++中的RAII机制指明”对象创建时通过构造函数进行初始化,析构时通过析构函数释放资源”,但实际中因类无构造函数时编译器将自动合成一个默认构造函数,该构造函数只服务于自身编译器构造需要而不负责对象成 ...

  5. 逆向实用干货分享,Hook技术第二讲,之虚表HOOK

    逆向实用干货分享,Hook技术第二讲,之虚表HOOK 正好昨天讲到认识C++中虚表指针,以及虚表位置在反汇编中的表达方式,这里就说一下我们的新技术,虚表HOOK 昨天的博客链接: http://www ...

  6. 初步学习C++中的继承关系

    继承机制是面向对象程序设计使代码能够复用的最重要的手段,它同意程序猿在保持原有类特性的基础上进行扩展,添加功能. 这样产生新的类,称派生类.继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂 ...

  7. C++继承、多态与虚表

    继承 继承的一般形式 子类继承父类,是全盘继承,将父类所有的东西都继承给子类,除了父类的生死,就是父类的构造和析构是不能继承的. 继承的访问权限从两方面看: 1.对象:对象只能直接访问类中公有方法和成 ...

  8. 图说C++对象模型:对象内存布局详解

    0.前言 文章较长,而且内容相对来说比较枯燥,希望对C++对象的内存布局.虚表指针.虚基类指针等有深入了解的朋友可以慢慢看. 本文的结论都在VS2013上得到验证.不同的编译器在内存布局的细节上可能有 ...

  9. C++STL - 模板的其他特性

    之前已经总结过函数模板和类模板了,对于模板还有一些其他的特性,这篇主要介绍这些特性.主要都是一些特殊情况. 模板的其他特性 1.缺省参数 (1)类模板的模板参数可以带有缺省值,实例化该模板时,如果提供 ...

  10. C/C++ 笔试题

    /////转自http://blog.csdn.net/suxinpingtao51/article/details/8015147#userconsent# 微软亚洲技术中心的面试题!!! 1.进程 ...

随机推荐

  1. HBase-统计表总行数的三种方式

    由于Hbase是列式数据库,没有提供类似SQL的数据查询语句,可以通过以下三种方式获取表的总行数. 1. 使用Hbase自带的Count命令 hbase提供了count命令可以在hbase交互界面使用 ...

  2. 使用 Etcd 快照文件恢复 Etcd 数据:应对单节点及高可用集群情况

    1.概述 在 Kubernetes 集群中,所有操作的资源数据都存储在 Etcd 数据库上.为了确保在节点故障.集群迁移或其他异常情况下能够尽快恢复集群数据,我们需要定期对 Etcd 数据进行容灾备份 ...

  3. UUID算法:独一无二的标识符解决方案

    引言 在分布式系统和大数据环境下,唯一标识符的生成和管理是一项关键任务.UUID(Universally Unique Identifier)算法应运而生,成为了解决重复数据和标识符冲突的有效工具.本 ...

  4. 压缩软件7-Zip的简单使用

    简介及下载地址 7-Zip是一款开源免费的压缩软件,支持windows系统及Linux系统,压缩后文件扩展名默认为.7z后缀.   网址: https://www.7-zip.org/ 截图如下: 也 ...

  5. Pandas字符串离散化处理

    字符串离散化处理 import pandas as pd import numpy as np from matplotlib import pyplot as plt # 读取csv文件 file_ ...

  6. Java设计模式-迭代器模式Iterator

    介绍 根据GoF的定义,迭代器模式提供了一种顺序访问聚合对象的元素而不暴露其底层表示的方法.这是一种行为设计模式. 顾名思义,迭代器有助于以定义的方式遍历对象集合,这对客户端应用程序很有用.在迭代期间 ...

  7. Oracle SQL 注入攻击

    All about Security - SQL Injection 最近做个一个有关ORACLE数据库安全的网上研讨.我们有1300多位参与者,反馈相当丰富. I recently did a we ...

  8. logback日志组件使用案例

    关于logback: http://www.cnblogs.com/warking/p/5710303.html 官方文档 使用logback: idea创建maven项目 修改pom.xml添加依赖 ...

  9. Java Socket编程系列(四)开发支持多客户端访问的Server

    例子来自Java官方教程,稍作调整. 上一篇介绍了单客户端访问的Server实现,这一篇实现的是多个客户端请求服务端,根据服务端提示进行一系列操作. 协议类(和系列三一样没变): package co ...

  10. centos上使用makefile编译sliver时 提示gcc 错误,cannot find -ldl cannot find -lpthread cannot find -lc

    github.com/bishopfox/sliver/server /usr/local/go/pkg/tool/linux_amd64/link: running gcc failed: exit ...