C++虛函數表(虛表Virtual Table,簡稱vtable、VFT)(編譯器為支持運行時多態(動態綁定)而自動生成的一種內部數據結構)虛函數指針vptr

文章目錄

      • **1. 虛函數表的核心概念**
        • - **虛函數表(vtable)**:
        • - **虛函數指針(vptr)**:
      • **2. 虛函數表的生成與工作流程**
        • **生成時機**
          • - **當一個類中至少有一個虛函數時**,編譯器會為該類生成一個虛函數表。
          • - **派生類繼承虛函數表**:
        • **調用流程**
          • 1. **對象創建時**:
          • 2. **調用虛函數時**:
      • **3. 示例代碼解析**
        • - 以下代碼展示了虛函數表的工作原理
        • - **虛函數表的生成**:
        • - **調用過程**:
      • **4. 虛函數表的內存布局**
        • - 以一個簡單的類為例
        • - **對象內存布局**:
        • - **虛函數表結構**:
      • **5. 虛函數表的注意事項**
        • 1. **性能開銷**:
        • 2. **純虛函數與抽象類**:
        • 3. **多繼承與虛函數表**:
        • 4. **編譯器相關**:
      • **6. 虛函數調用流程圖**
      • **7. 總結**

C++中的 虛函數表(Virtual Table,簡稱 vtable)是編譯器為支持 運行時多態(動態綁定)而自動生成的一種內部數據結構。它的核心作用是通過 虛函數指針(vptr)和虛函數表的配合,實現基類指針或引用調用派生類重寫函數的能力。


1. 虛函數表的核心概念

- 虛函數表(vtable)

每個包含虛函數的類都會有一個虛函數表。這個表是一個指針數組,存儲了該類所有虛函數的地址。例如:

  • 如果一個類有3個虛函數,虛函數表就包含3個指針,分別指向這3個函數的實現。
  • 如果派生類重寫了某個虛函數,派生類虛函數表中對應位置的指針會被替換為派生類的函數地址。
- 虛函數指針(vptr)

每個包含虛函數的對象在內存中會隱式地包含一個指針(vptr),指向該對象所屬類的虛函數表。

  • vptr通常位于對象內存布局的最前面(具體位置可能因編譯器而異)。
  • 當通過基類指針或引用調用虛函數時,程序會通過vptr查找虛函數表,找到正確的函數地址并執行。

2. 虛函數表的生成與工作流程

生成時機
- 當一個類中至少有一個虛函數時,編譯器會為該類生成一個虛函數表。
- 派生類繼承虛函數表
  • 如果派生類沒有重寫基類的虛函數,則虛函數表中直接繼承基類的虛函數地址。
  • 如果派生類重寫了某個虛函數,則虛函數表中對應位置的指針會被更新為派生類的函數地址。
調用流程
1. 對象創建時

編譯器會自動初始化對象的vptr,使其指向該類的虛函數表。

2. 調用虛函數時
  • 程序通過對象的vptr找到虛函數表。
  • 根據虛函數表中的索引(與函數聲明順序一致)找到對應的函數地址。
  • 調用該地址指向的函數(可能是基類或派生類的實現)。

3. 示例代碼解析

- 以下代碼展示了虛函數表的工作原理
#include <iostream>
using namespace std;class Base {
public:virtual void func() { cout << "Base::func()" << endl; }
};class Derived : public Base {
public:void func() override { cout << "Derived::func()" << endl; }
};int main() {Base* ptr = new Derived();ptr->func();  // 輸出 "Derived::func()"delete ptr;return 0;
}
- 虛函數表的生成
  • Base類有一個虛函數func(),因此編譯器為Base生成一個虛函數表,表中存儲Base::func()的地址。
  • Derived類重寫了func(),因此編譯器為Derived生成一個新的虛函數表,表中存儲Derived::func()的地址。
- 調用過程
  • ptrBase*類型,但指向Derived對象。
  • 調用ptr->func()時,程序通過Derived對象的vptr找到Derived的虛函數表,調用Derived::func()

4. 虛函數表的內存布局

- 以一個簡單的類為例
class Base {
public:virtual void func1() {}virtual void func2() {}
};
- 對象內存布局

每個Base對象的內存布局如下:

[vptr] -> 指向Base的虛函數表
[其他成員變量]
Base
+virtual void func1()
+virtual void func2()
?data?
BaseObject
vptr --> Base
data1: int
data2: double
- 虛函數表結構
Base的虛函數表:
+-----------------+
| func1() 的地址  |
+-----------------+
| func2() 的地址  |
+-----------------+
Base的虛函數表
func1() 地址
func2() 地址

5. 虛函數表的注意事項

1. 性能開銷
  • 調用虛函數需要通過兩次間接尋址(vptr → 虛函數表 → 函數地址),比直接調用普通函數稍慢。
  • 每個對象需要額外存儲一個vptr,增加了內存占用。
2. 純虛函數與抽象類
  • 如果類中包含純虛函數(virtual void func() = 0;),則該類為抽象類,不能實例化對象。
  • 純虛函數在虛函數表中通常用特殊標記(如NULL)表示。
3. 多繼承與虛函數表
  • 多繼承情況下,對象可能包含多個vptr,分別指向不同基類的虛函數表。
  • 虛函數表的管理會更復雜,但核心原理與單繼承相同。
Derived對象
vptr1 (Base1)
vptr2 (Base2)
Base1::func()
Base2::func()
4. 編譯器相關
  • 虛函數表是編譯器的實現細節,C++標準未規定具體實現方式。主流編譯器(如GCC、MSVC)均采用類似機制。

6. 虛函數調用流程圖

以下是虛函數調用的完整流程:

Base* ptr = new Derived();
ptr->func()
vptr 指向 Derived 的虛函數表?
查找虛函數表中 func() 的地址
調用 Derived::func()
查找虛函數表中 func() 的地址
調用 Base::func()

7. 總結

虛函數表是C++實現運行時多態的核心機制。通過虛函數表和虛函數指針的配合,C++能夠在運行時根據對象的實際類型動態選擇正確的函數實現。這種機制雖然帶來了一定的性能和內存開銷,但極大地增強了代碼的靈活性和可擴展性,是面向對象編程中多態特性的基石。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/82722.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/82722.shtml
英文地址,請注明出處:http://en.pswp.cn/web/82722.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用Python和TensorFlow實現圖像分類

最近研學過程中發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊鏈接跳轉到網站人工智能及編程語言學習教程。讀者們可以通過里面的文章詳細了解一下人工智能及其編程等教程和學習方法。下面開始對正文內容的…

Unity UI 性能優化--Sprite 篇

&#x1f3af; Unity UI 性能優化終極指南 — Sprite篇 &#x1f9e9; Sprite 是什么&#xff1f;—— 渲染的基石與性能的源頭 在Unity的2D渲染管線中&#xff0c;Sprite 扮演著至關重要的角色。它不僅僅是2D圖像資源本身&#xff0c;更是GPU進行渲染批處理&#xff08;Batch…

【git】把本地更改提交遠程新分支feature_g

創建并切換新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “實現圖片上傳功能” 推送到遠程 git push -u origin feature_g

vue中加載Cesium地圖(天地圖、高德地圖)

目錄 1、將下載的Cesium包移動至public下 2、首先需要將Cesium.js和widgets.css文件引入到 3、 新建Cesium.js文件&#xff0c;方便在全局使用 4、新建cesium.vue文件&#xff0c;展示三維地圖 1、將下載的Cesium包移動至public下 npm install cesium后??????? 2、…

Elasticsearch的插件(Plugin)系統介紹

Elasticsearch的插件(Plugin)系統是一種擴展機制,允許用戶通過添加自定義功能來增強默認功能,而無需修改核心代碼。插件可以提供從分析器、存儲后端到安全認證、機器學習等各種功能,使Elasticsearch能夠靈活適應不同的應用場景和業務需求。 一、插件的核心特點 模塊化擴展…

基于 openEuler 22.03 LTS SP1 構建 DPDK 22.11.8 開發環境指南

基于 openEuler 22.03 LTS SP1 構建 DPDK 22.11.8 開發環境指南 本文詳細介紹了在 openEuler 22.03 LTS SP1 操作系統上構建 DPDK 22.11.8 開發環境的完整流程。DPDK 20 版本之后采用 mesonninja 的編譯方式&#xff0c;與早期版本有所不同。本文內容也可作為其他 Linux 發行版…

微服務網關SpringCloudGateway+SaToken鑒權

目錄 概念 前置知識回顧 拿到UserInfo 用于自定義權限和角色的獲取邏輯 最后進行要進行 satoken 過濾器全局配置 概念 做權限認證的時候 我們首先要明確兩點 我們需要的角色有幾種 我們需要的權限有幾種 角色 分兩種 ADMIN 管理員 &#xff1a;可管理商品 CUSTIOMER 普通…

Spring Cloud Gateway 中自定義驗證碼接口返回 404 的排查與解決

Spring Cloud Gateway 中自定義驗證碼接口返回 404 的排查與解決 問題背景 在一個基于 Spring Cloud Gateway WebFlux 構建的微服務項目中&#xff0c;新增了一個本地驗證碼接口 /code&#xff0c;使用函數式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…

Dify中聊天助手、agent、文本生成、chatflow、工作流模式解讀分析與對比

一次解讀 1. 聊天助手 (Chat Assistant) 情景定位 (Situation): 你需要創建一個可以與用戶進行多輪對話的AI應用&#xff0c;例如客服機器人、信息查詢助手、或一個特定領域的虛擬專家。目標明確 (Purpose): 核心目標是理解并響應用戶的連續提問&#xff0c;維持對話的上下文…

使用Node.js分片上傳大文件到阿里云OSS

阿里云OSS的分片上傳&#xff08;Multipart Upload&#xff09;是一種針對大文件優化的上傳方式&#xff0c;其核心流程和關鍵特性如下&#xff1a; 1. ?核心流程? 分片上傳分為三個步驟&#xff1a; 初始化任務?&#xff1a;調用InitiateMultipartUpload接口創建上傳任務…

C++ if語句完全指南:從基礎到工程實踐

一、選擇結構在程序設計中的核心地位 程序流程控制如同城市交通網絡&#xff0c;if語句則是這個網絡中的決策樞紐。根據ISO C標準&#xff0c;選擇結構占典型項目代碼量的32%-47%&#xff0c;其正確使用直接影響程序的&#xff1a; 邏輯正確性 執行效率 可維護性 安全邊界 …

【大模型LLM學習】Flash-Attention的學習記錄

【大模型LLM學習】Flash-Attention的學習記錄 0. 前言1. flash-attention原理簡述2. 從softmax到online softmax2.1 safe-softmax2.2 3-pass safe softmax2.3 Online softmax2.4 Flash-attention2.5 Flash-attention tiling 0. 前言 Flash Attention可以節約模型訓練和推理時間…

python打卡day46@浙大疏錦行

知識點回顧&#xff1a; 不同CNN層的特征圖&#xff1a;不同通道的特征圖什么是注意力&#xff1a;注意力家族&#xff0c;類似于動物園&#xff0c;都是不同的模塊&#xff0c;好不好試了才知道。通道注意力&#xff1a;模型的定義和插入的位置通道注意力后的特征圖和熱力圖 內…

JavaSec-SPEL - 表達式注入

簡介 SPEL(Spring Expression Language)&#xff1a;SPEL是Spring表達式語言&#xff0c;允許在運行時動態查詢和操作對象屬性、調用方法等&#xff0c;類似于Struts2中的OGNL表達式。當參數未經過濾時&#xff0c;攻擊者可以注入惡意的SPEL表達式&#xff0c;從而執行任意代碼…

SpringCloud——OpenFeign

概述&#xff1a; OpenFeign是基于Spring的聲明式調用的HTTP客戶端&#xff0c;大大簡化了編寫Web服務客戶端的過程&#xff0c;用于快速構建http請求調用其他服務模塊。同時也是spring cloud默認選擇的服務通信工具。 使用方法&#xff1a; RestTemplate手動構建: // 帶查詢…

【深入學習Linux】System V共享內存

目錄 前言 一、共享內存是什么&#xff1f; 共享內存實現原理 共享內存細節理解 二、接口認識 1.shmget函數——申請共享內存 2.ftok函數——生成key值 再次理解ftok和shmget 1&#xff09;key與shmid的區別與聯系 2&#xff09;再理解key 3&#xff09;通過指令查看/釋放系統中…

探索 Java 垃圾收集:對象存活判定、回收流程與內存策略

個人主頁-愛因斯晨 文章專欄-JAVA學習筆記 熱門文章-賽博算命 一、引言 在 Java 技術體系里&#xff0c;垃圾收集器&#xff08;Garbage Collection&#xff0c;GC&#xff09;與內存分配策略是自動內存管理的核心支撐。深入探究其原理與機制&#xff0c;對優化程序內存性能…

hbase資源和數據權限控制

hbase適合大數據量下點查 https://zhuanlan.zhihu.com/p/471133280 HBase支持對User、NameSpace和Table進行請求數和流量配額限制&#xff0c;限制頻率可以按sec、min、hour、day 對于請求大小限制示例&#xff08;5K/sec,10M/min等&#xff09;&#xff0c;請求大小限制單位如…

大數據-275 Spark MLib - 基礎介紹 機器學習算法 集成學習 隨機森林 Bagging Boosting

點一下關注吧&#xff01;&#xff01;&#xff01;非常感謝&#xff01;&#xff01;持續更新&#xff01;&#xff01;&#xff01; 大模型篇章已經開始&#xff01; 目前已經更新到了第 22 篇&#xff1a;大語言模型 22 - MCP 自動操作 FigmaCursor 自動設計原型 Java篇開…

Delphi 實現遠程連接 Access 數據庫的指南

方法一&#xff1a;通過局域網共享 Access 文件&#xff08;簡單但有限&#xff09; 步驟 1&#xff1a;共享 Access 數據庫 將 .mdb 或 .accdb 文件放在局域網內某臺電腦的共享文件夾中。 右鍵文件夾 → 屬性 → 共享 → 啟用共享并設置權限&#xff08;需允許網絡用戶讀寫&a…