【C++進階】一文吃透靜態綁定、動態綁定與多態底層機制(含虛函數、vptr、thunk、RTTI)

【C++進階】一文吃透靜態綁定、動態綁定與多態底層機制(含虛函數、vptr、thunk、RTTI)

作者:你的C++教練
日期:2025-08-01


目錄

  1. 靜態綁定 vs 動態綁定
  2. 非虛函數的三大坑
  3. 多態的四要素
  4. 虛析構函數為什么必須寫?
  5. 探秘 vptr/vftablethunk
  6. RTTI 與 dynamic_cast 的底層真相
  7. 虛繼承下的虛表偏移
  8. 性能、inline 與構造語義
  9. 實戰代碼與匯編級分析


1?? 靜態綁定 vs 動態綁定

綁定類型決定時機典型場景性能
靜態綁定 (Static Binding)編譯期普通成員函數、缺省實參直接 call,零額外開銷
動態綁定 (Dynamic Binding)運行期虛函數通過指針/引用調用一次 vptr 解引用 + 間接 call

一句話:“指針/引用 + 虛函數”才會觸發動態綁定,否則全是靜態綁定。


2?? 非虛函數的三大坑

① 普通函數靜態綁定
struct B { void foo() { puts("B"); } };
struct D : B { void foo() { puts("D"); } };B* p = new D;
p->foo();          // 輸出 B!(靜態綁定)
② 缺省實參靜態綁定
struct B {virtual void f(int x = 1) { cout << x; }
};
struct D : B {void f(int x = 2) override { cout << x; }
};B* p = new D;
p->f();  // 輸出 1!缺省值來自 B 的定義
③ 非虛析構函數 → 內存泄漏
B* p = new D;
delete p;   // 只調 ~B(),~D() 不會被調用

3?? 多態的四要素

條件說明
繼承存在父子類
虛函數父類至少一個 virtual
重寫子類覆蓋父類虛函數
指針/引用用父類指針/引用指向子類對象

示例:

class A { public: virtual void vf() { puts("A"); } };
class B : public A { void vf() override { puts("B"); } };A* p = new B;
p->vf();   // 動態綁定,輸出 B

4?? 為什么必須寫虛析構函數?

Base* p = new Derived;
delete p;  // 只有 ~Base() 是 virtual,才會:
  1. 先通過 vptr 找到 Derived::~Derived
  2. 執行 ~Derived
  3. 自動插入 ~Base()
  4. 最終 operator delete 釋放內存

結論:任何可能被繼承的類,析構函數都寫成 virtual


5?? 探秘 vptr / vftable / thunk

對象模型(簡化)
對象地址
├─ vptr ----┐
├─ 成員變量 │
│           │
v           v+--------------+
vftable | &Base::foo   |  <-- 如果未被覆蓋+--------------+| &Derived::bar|+--------------+
thunk 是什么?
  • 當用 第二基類指針 指向多重繼承的子對象時,需要調整 this 偏移。
  • 編譯器生成一段 匯編樁代碼(thunk)放在虛表中:
    thunk:sub  this, offset   ; 調整 thisjmp  Derived::foo   ; 真正虛函數
    
  • 虛表項指向的就是 thunk 地址。

6?? RTTI 與 dynamic_cast

if (Derived* d = dynamic_cast<Derived*>(basePtr)) {d->onlyInDerived();
}
實現原理
  • 每個有虛函數的類都會在虛表 -1 位置type_info 指針。
  • dynamic_cast 通過 vptr[-1] 比較 RTTI 信息,決定轉換是否成功。

7?? 虛繼承下的虛表偏移

struct VBase { virtual void vf(); };
struct A : virtual VBase { void vf() override; };
  • 虛基類子對象在內存中可能位于 對象尾部
  • vptr 需要 間接尋址 才能找到虛基類中的虛函數,帶來額外一次指針解引用。

8?? 性能 & inline 提醒

因素開銷
虛函數調用一次額外內存讀取
多重繼承可能增加 thunk
虛繼承兩次指針解引用
inline 失敗遞歸、過大、地址取址都會阻止

建議:性能關鍵路徑避免深度虛繼承,熱點函數盡量 final/inline


9?? 構造語義 & 匯編級分析

偽代碼回顧
C::C() {B::B();               // 基類構造A::A();           // 再基類vptr = A::vftable;vptr = B::vftable;vptr = C::vftable;    // 最終態
}
  • 構造期間 對象類型不斷變化,虛表指針逐級覆蓋。
  • 析構期間 反向逐級回退,保證 dynamic_cast/typeid 行為正確。

🔚 結論速記

規則口訣
需要多態“指針引用 + virtual”
析構函數“能繼承就 virtual”
缺省實參“靜態綁定,別在虛函數里玩默認值”
RTTI“至少一個 virtual 才能 dynamic_cast
性能“虛函數=一次間接尋址,虛繼承=兩次”

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

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

相關文章

VUE基礎知識2

1.計算屬性&#xff1a;使用計算屬性來描述依賴響應式狀態的復雜邏輯。關鍵字computed:{}//計算屬性&#xff0c;使用的時候和函數方法不一樣&#xff0c;不需要加括號。簡單來說就是模板方法的復雜邏輯放到了計算屬性中去。2.計算屬性緩存VS方法&#xff1a;計算屬性值會基于其…

在PyCharm中將現有Gitee項目重新上傳為全新項目

如果你想將當前本地的Gitee項目重新上傳為一個全新的Gitee項目&#xff08;保留本地代碼但斷開與原倉庫的關聯&#xff09;&#xff0c;可以按照以下步驟操作&#xff1a; 刪除舊的Git遠程倉庫關聯 打開PyCharm&#xff0c;進入你的項目 點擊頂部菜單 Git > Manage Remotes …

設計模式1:創建型模式

設計模式1&#xff1a;創建型模式 設計模式2&#xff1a;結構型模式&#xff08;編寫中&#xff09; 設計模式3&#xff1a;行為型模式&#xff08;編寫中&#xff09; 前言 設計模式是軟件開發中經過驗證的可復用解決方案&#xff0c;它們源自實踐、提煉于經驗&#xff0c;并…

React--》規劃React組件庫編碼規范與標準 — Button篇

目前前端組件化已經成為前端開發的核心思想之一&#xff0c;在這篇文章中將深入探討如何規劃一個規范的Button組件&#xff0c;讓它不僅能高效支持不同的功能需求還能確保跨項目、跨團隊的一致性&#xff0c;拋磚引玉的方式引出后面組件庫的其他組件的開發&#xff01; 目錄 B…

中科米堆CASAIM金屬件自動3d測量外觀尺寸三維檢測解決方案

金屬零部件的外觀尺寸檢測直接關系到產品的裝配精度和使用性能。CASAIM基于激光掃描技術的自動化三維掃描系統&#xff0c;為金屬加工行業提供了高效的自動3D測量解決方案&#xff0c;有效解決了傳統檢測方式效率低、覆蓋面有限等問題。激光掃描技術在金屬件測量中優勢明顯。與…

開源數據同步中間件,支持MySQL、Oracle

DBSyncer&#xff08;英[dbs??k??(r)]&#xff0c;美[dbs??k??(r) 簡稱dbs&#xff09;是一款開源的數據同步中間件&#xff0c;提供MySQL、Oracle、SqlServer、PostgreSQL、Elasticsearch(ES)、Kafka、File、SQL等同步場景。支持上傳插件自定義同步轉換業務&#xff0…

中英混合的語音識別XPhoneBERT 監督的音頻到音素的編碼器結合 f0 特征LID

完整項目包獲取點擊文末名片完成一個 Code-Switching&#xff08;中英混合&#xff09;的語音識別系統&#xff0c;整個流程如下思路進行&#xff1a; 163. (Step 1) 訓練音頻到音素的編碼器&#xff08;Audio → Phoneme Encoder&#xff09; 你已經完成了此部分。核心思路是利…

Param關鍵字的使用

1&#xff1a;當一個方法的某一個參數個數不固定的時候&#xff0c;可以使用Param2:可變的方法參數必須定義為數組類型3&#xff1a;該參數必須放在方法參數的最后&#xff0c;應且只有一個4&#xff1a;參數必須為一維數組5&#xff1a;params不能和ref和out組合使用namespace…

京東云輕量云服務器與騰訊云域名結合配置網站及申請SSL證書流程詳解

京東云輕量云服務器與騰訊云域名結合配置網站及申請SSL證書流程詳解 1. 需求及實現效果 1.1. 需求 先說一下我當前情況&#xff0c;我目前有一個京東云服務器和一個在騰訊云旗下買的域名&#xff08;不要問為啥一個在京東云&#xff0c;一個在騰訊云&#xff0c;那自然是哪個…

Python入門Day14:面向對象編程初步(OOP入門)

學習目標&#xff1a;理解面向對象編程&#xff08;OOP&#xff09;的基本思想&#xff1a;類&#xff0c;對象掌握類的定義&#xff0c;構造方法&#xff0c;實例屬性和方法熟悉self的含義與作用學會用類組織和封裝代碼&#xff0c;初步構建自己的“對象世界”一、什么是面向對…

日志和指標標簽規范化方案

好的&#xff0c;設計一個有效的日志和指標標簽規范化方案對于構建可觀測性強、易于維護、關聯分析順暢的系統至關重要。混亂的標簽命名會極大增加查詢、聚合、告警和故障排除的難度。 以下是一個綜合性的標簽規范化方案建議&#xff0c;結合了行業最佳實踐&#xff1a; 核心目…

Windows和Linux的tree工具

目錄 1.前言 2.Linux的tree工具 2.1.安裝tree 2.2.常用命令與參數 2.3.常見應用場景 2.4.注意事項 3.Windows的tree工具 3.1.基礎語法 3.2.核心參數詳解 3.3.常見應用場景 3.4.局限性與增強方案 4.Windows 與 Linux tree 的核心差異 5.tree工具優勢 5.總結 相關…

[echarts] 更新數據

option {title: { text: 銷售數據 },tooltip: { trigger: axis },legend: { data: [銷量, 庫存] },xAxis: {type: category,data: [襯衫, 羊毛衫, 雪紡衫]},yAxis: { type: value },series: [{ name: 銷量, type: bar, data: [5, 20, 36] },{ name: 庫存, type: line, data: […

通過el-image實現點擊文字查看圖片,及其圖片列表

場景一&#xff1a;表格中有時候會有點擊文字查看圖片的功能&#xff08;因為表格的一個單元格不方便顯示多個圖片&#xff09;如下圖所示&#xff1a;對于這個需求&#xff0c;我們可以應對的方案是&#xff1a;在文字旁邊寫一個el-image圖默認顯示多張圖片中的第一張&#xf…

003 實習(前端jquery之輪播圖,學校網頁)

web前端,查詢官網:w3schoolHTML:負責網頁結構&#xff08;頁面元素和內容&#xff09;CSS:負責網頁的表現&#xff08;網頁元素的外觀、位置等頁面樣式&#xff0c;如顏色&#xff0c;大小&#xff09;JAVAScript:負責網頁的行為&#xff08;交互效果&#xff09;<a>:超鏈…

Mysql group by

臨時表與內存表 內存表是 Memory 引擎表&#xff0c;表的數據行都在內存。 臨時表可以使用各種引擎。 臨時表是線程私有表&#xff0c;其他線程不可見&#xff0c;不需考慮重名問題。 session 結束時臨時表會被自動刪除。 如果 Binlog_format row&#xff0c;則臨時表語句不進…

Linux(15)——進程間通信

目錄 一、進程間通信的介紹 ??進程間通信的目的 ??進程間通信的本質 進程間通信的分類 ??管道 ??System V IPC ??POSIX IPC 二、管道 &#x1f9e0;什么是管道 ??匿名管道 &#x1f4dd;匿名管道的原理 &#x1f4dd;pipe函數 &#x1f4dd;匿名管道…

【Flutter】雙路視頻播放方案

最近在做雙路視頻播放&#xff0c;就是在一個頁面播放兩個視頻。我遇到的問題就是音頻焦點沖突問題&#xff0c;在下面說明。什么是雙路視頻播放&#xff08;來自AI&#xff09;雙路視頻播放&#xff08;Dual-Video Playback&#xff09;&#xff0c;從字面上理解&#xff0c;就…

筆試——Day25

文章目錄第一題題目思路代碼第二題題目&#xff1a;思路代碼第三題題目&#xff1a;思路代碼第一題 題目 笨小猴 思路 模擬 統計每個字符出現的次數&#xff0c;用最大減最小&#xff0c;判斷是不是質數&#xff1b; 質數的判斷使用試除法&#xff1b; 代碼 第二題 題目&…

【C#學習Day15筆記】拆箱裝箱、 Equals與== 、文件讀取IO

前言在C#第15天的學習中&#xff0c;我深入探索了類型轉換機制、對象比較原理和文件操作技術三大核心主題。這些知識是構建高效、健壯程序的關鍵基礎。本文完整保留我的課堂實踐代碼和命名體系&#xff0c;通過結構化梳理幫助大家掌握這些核心概念。所有代碼示例均來自我的實際…