簡體   English   中英

派生類可以有多個指向虛擬表的指針嗎?

[英]Can derived classes have more than one pointer to a virtual table?

我正在觀看 BackToBasics 演講:CppCon2019 的Virtual Dispatch 及其替代方案。 演示者說和幻燈片顯示(假設我沒有誤解)派生的 class 從基礎 class 繼承了一個 vtable 指針,並且還具有自己的 vptr。

當然,從技術上講,這不是標准規定的,但我讓自己有點困惑,我對 sizeof() 的實驗似乎也暗示應該只需要一個指針。 請有人澄清一下是否有需要多個 vptr 的情況?

謝謝

PS 為了清楚起見,在這種情況下,我們正在考慮更常見的公共 inheritance,而不是虛擬或多個 inheritance(演講者在演講的前面部分明確提到了這一點)。

vtable 包含已知偏移量處 class 的每個虛擬 function 的地址。

[備注:在實踐中,與常規的 class 不同,vtables 具有負偏移量的成員,很像數組中間的指針。 這只是一個不會太大改變實現自由的約定。 無論如何,唯一的問題是在 vtable 中放置信息是由約定(ABI)規定的,編譯器遵循相同的規則為多態類生成兼容的代碼。]

當您在派生的 class 中具有附加功能時會發生什么? (不僅僅是從基類“繼承”的函數)

一旦您接受指向結構的指針都指向整個 object 及其第一個成員的想法,您就會認為指向派生 class 的指針指向位於零偏移處的基本 class。 因此,您可以擁有完全相同的指針值,表示為void* ,可以將其用於派生的 object 或根據此約定的單個 inheritance 的基數

現在,您可以將其應用於任何數據結構,甚至應用於實際上不是表(相同類型的元素數組或可以以相同方式解釋的值的數組)而是記錄(不相關類型的對象或意義); 您可以看到,此類派生的 class 的 vtable 可以以完全相同的方式從其唯一基礎的 vtable 派生。

(注意,如果你把 C++ 編譯成 C,你在做這些事情的時候可能會遇到類型別名規則。當然匯編沒有這樣的問題,也沒有天真的編譯“高級匯編程序”Z0D61F8370CAD1432F8ZB8)。

因此,對於單個 inheritance,基礎集成並優化到派生的 class 中:

  • 用於實例的數據成員(class 類型)
  • 對於虛函數成員,即 vtable 的數據成員(或元 class 的成員,如果您想像的話)。

請注意,將 base 放置在 offset 0 處允許您將 vtable base 放置在 offset 0 處,這反過來又允許您使用相同的 vptr 但並不暗示它; 相反,與基礎共享 vptr 意味着基礎 vtable 位於偏移量零(vtable 布局 = 元 class 級別),因此基礎必須位於偏移量零(數據成員布局 = class 級別)。

並且多個 inheritance 實際上是單個 inheritance 加,因為一個 class 始終被視為特權:它放置在偏移量零處,因此指針相同,因此指針可以放置在零偏移量處(可以使用相同的偏移量 vtable) 其他基地,並非如此。

正如我們所看到的,除了一個繼承的多態類之外,所有繼承的多態類都放置在多個 inheritance 中的非零偏移處。 每個都在派生的 class 中攜帶一個額外的“繼承”vptr; 該(隱藏)指針成員必須由任何派生構造函數正確填充。

這些額外的 vptr 用於發生在非零偏移量的基類,因此必須調整指向繼承基的指針(添加一個正常量以轉換為基指針,刪除它以轉換回來)。 編譯器需要生成代碼來執行隱式轉換是一件小事(將 integer 轉換為浮點類型是一項更復雜的任務); but here the conversion of this is between a function call on a given base type and landing in the function that is an overrider in a base or derived class: the difference is that adjustment depends on function overriding which is only known for a class (an元類型的實例)。 所以vptr 需要指向不同的 vtable 信息:知道如何處理這些基礎到派生指針的轉換。

作為“元類型”的實例,vtables 擁有自動調整所有指針的所有信息。 (這些取決於所涉及的特定 class 類型,而不取決於其他因素。)

所以在實現層面,兩種類型的繼承是:

  • 零偏移 inheritance; 共享 vptr; 在某些 vtable 和 ABI 描述中稱為主要基礎 class;
  • 任意偏移 inheritance; 擁有另一個 vptr; 稱為二級鹼基 class。

這是基本的東西。 虛擬 inheritance 在實現級別上要微妙得多,甚至主要的概念也不是很清楚,因為虛擬基只能在一些派生類中成為派生 class 的“主要”!

假設我們有兩個類,每個類至少有一個虛擬 function, PossessionVehicle 要為其中任何一個的派生 class 的實例調用這些虛擬函數,需要指向虛擬表的指針。 由於這兩個類是獨立的,它們的虛擬表將完全不同。

現在想象OwnedVehicle派生自PossessionVehicle 要在Possession中為OwnedVehicle實例調用虛擬 function ,需要指向Possession所需類型的虛擬 function 表的指針。 類似地,要在Vehicle中為 OwnedVehicle 的實例調用虛擬OwnedVehicle需要一個指向Vehicle所需類型的虛擬 function 表的指針。

典型的實現通過為OwnedVehicle構建一個虛擬 function 表來處理此問題,該表包含OwnedVehicle虛擬功能(如果有)的一部分, Vehicle虛擬功能的一部分和Possession虛擬功能的一部分。 然后,當從指向不同類型的 object 的指針調用虛擬 function 時,編譯器所要做的就是將適用的增量應用於指向它的虛擬 function 的正確部分表指針。

雖然多個 inheritance 的情況更復雜,但僅單個 inheritance 也會出現同樣的情況。 OwnedVehicle 的虛擬OwnedVehicle表在其內部包含一個用於Vehicle的虛擬 function 表,即使不涉及Possession也會這樣做。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM