**:
1.虛函式
·虛表是怎麼實現的?
虛表存放在**?
·虛表中的資料是在什麼時候確定的?
·物件中的虛表指標又在什麼時候賦值的?
我們很難通過
c++語言本身來找到答案。
c++標準給編譯器實現者定義了語法規範,
但是被並沒有定義如何實現這些語法規範,不同的編譯器實現者可能有不同的實現方法,可以肯定的是他們的編譯器必須符合這些語法規範。
組合語言作為最接近機器語言的計算機語言,
可以為我們揭示一些隱藏在編譯器內部的細節。
接下來本來就試圖通過對
c++原始碼進行反彙編的方式來解答這些疑惑。
二、分析
這裡我選用 winxp 和 vs2008 作為我們這次分析的平台。我們建立乙個最簡單的 win32 控制台程式,並定義兩個簡單的類:
接下來我們可以直接編譯這些 c++原始碼就可以得到相應的彙編**。 通過分析這些彙編**我們就找到許多有用的資訊。我們可以找到這樣的彙編**:
以上的彙編**定義了兩個資料段, 而這兩個資料段中的內容恰好就是類的虛表。 至此虛表的"廬山真面目"完全展示在我們的面前。 根據這些資訊,我們可以推理出很多有用的結論:
·擁有虛函式的類會有乙個虛表,
而且這個虛表存放在類定義模組的資料段中。
模組的資料段通常存放定義在該模組的全域性資料和靜態資料,
這樣我們可以把虛表看作是模組的全域性資料或者靜態資料
·類的虛表會被這個類的所有物件所共享。
類的物件可以有很多,但是他們的虛表指標都指向同乙個虛表,從這個意義上說,我們可以把虛表簡單理解為類的靜態資料成員。
值得注意的是,雖然虛表是共享的,但是虛表指標並不是,類的每乙個物件有乙個屬於它自己的虛表指標。
·虛表中存放的是虛函式的位址。
另外乙個大的疑惑就是物件的虛表指標是在什麼時候被賦值的? 我們都知道,類的物件是通過建構函式來完成初始化,但是我們從來沒有在建構函式中初始化虛表指標, 那麼編譯器在幕後又做了哪些事情呢? 我們依然還是通過反彙編來找到答案。 在這個控制台程式的 main 函式中我們構建乙個類物件:
類的非靜態成員函式呼叫時,編譯器會傳入乙個"隱藏"的引數。 這個引數就是通常我們說的"this"指標,它的值就是物件的位址。 在上面的**中,暫存器 ecx 儲存的就是這個"
this" 指 針 , 同 時 它 的 值 又 賦 給 了 寄 存 器 eax。"??_7cd-szbase@@6b@"就是上面提到的虛表,同時它也代表了虛表的位址。
接下來,虛表的位址被賦給了由暫存器 eax 指定的記憶體中。由此可見,虛表的位址被存放在物件的起始位置,即物件的第乙個資料成員就是它的虛表指標。 同時我們還可以注意到,虛表指標的初始化確實發生在建構函式的呼叫過程中, 但是在執行建構函式體之前,即進入到建構函式的""之前。 為了更好的理解這一問題, 我們可以把建構函式的呼叫過程細分為兩個階段,即:
1.進入到建構函式體之間。
在這個階段如果存在虛函式的話,虛表指標被初始化。
如果存在建構函式的初始化列表的話,初始化列表也會被執行。 2.
進入到建構函式體內。
這一階段是我們通常意義上說的建構函式
簡單的搞個基類base;public:int a;static b;}
定義乙個物件 b b;除錯狀態下就可以看到b包含了什麼
類中只有虛表指標和普通成員(包括
const
成員)而普通函式,靜態成員是不在類中的.
類的初始化時機
什麼情況下需要開始類載入過程的第乙個階段 載入 虛擬機器規範中並沒強行約束,這點可以交給虛擬機器的的具體實現自由把握,但是對於初始化階段虛擬機器規範是嚴格規定了如下幾種情況,如果類未初始化會對類進行初始化。建立類的例項 訪問類的靜態變數 除常量 被final修辭的靜態變數 原因 常量一種特殊的變數,...
資源的初始化時機
zend framework 中的資源是什麼時候初始化的呢?還是直接上 來看比較有說服力 protected function bootstrap resource null foreach this getpluginresourcenames as resource elseif is stri...
JVM中類的初始化時機
只有對類進行主動引用,才會觸發其初始化方法,而除此之外的引用方式稱之為被動引用,不會觸發其初始化方法 這裡先定義乙個utils 名字隨便起的,不要見怪 作為被測試類 package day12 author weihuanwen date created in 2019 5 23 00 14 des...