您好,登錄后才能下訂單哦!
上一篇文章中,我們介紹了怎么從一個DLL中導出C++類,及選擇性導出C++類的成員的方法。那么,整個系統的底層機制是怎么樣的?是通過什么途徑,使得我們可以在另一個程序中使用一個DLL中導出的類的呢?
我們知道,要使用一個C++類,必要的條件是在編譯期能得到這個類的頭文件,并在鏈接期可以找到對應的符號的鏈接地址(比如成員函數、靜態數據成員等)。如果這個C++類與你的使用者在同一個工程,那這個條件很好滿足:
首先,C++類的頭文件很好獲得。直接在使用者那里將類的頭文件include即可
其次,C++類往往被編譯器作為一個編譯單元,生成一個obj文件。在最后進行鏈接的過程中,鏈接器會把工程中所有的obj鏈接以生成最終的二進制目標文件。所以鏈接器在遇到一處對類成員函數(或其它形式的符號引用)時,會在這個類生成的obj文件中找到符號的鏈接地址。
那么,在代碼中使用一個C++類,編譯期和鏈接期需要的到底是些什么東西呢?換句話說,滿足了什么樣的條件,編譯器和鏈接器就不會抱怨了呢?
根據C++語言的定義,一個C++類實際上是聲明或定義了如下幾類內容:
1. 聲明了一個數據結構,類中的非靜態數據成員、代碼中看不到但如果有虛函數就會生成的虛表入口地址指針等。
2. 聲明并定義了一堆函數,它們第一個參數都是一個指向這個數據結構的指針。這些實際上就是類中那些非靜態成員函數(包括虛函數),它們雖然在類聲明中是寫在類的一對大括號內部,但實際上沒有任何東西被加到前面第1條中所說的內部數據結構中。實際上,這樣的聲明只是為這些函數增加了兩個屬性:函數名標識符的作用域被限制在類中;函數第一個參數是this,被省略不寫了。
3. 聲明并定義了另一堆函數,它們看上去就是一些普通函數,與這個類幾乎沒有關系。這些實際上就是類中那些靜態函數,它們也是一樣,不會在第1條中所說的內部數據結構中增加什么東西,只是函數名標識符的作用域被限制在類中。
4. 聲明并定義了一堆全局變量。這些實際上就是類中那些靜態數據成員。
5. 聲明并定義了一個全局變量,此全局變量是一個函數指針數組,用來保存此類中所有的虛函數的入口地址。當然,這個全局變量生成的前提是這個類有虛函數。
下面是一個例子。
class MyClass { public: int x; int y; void Foo(); void Bar(int newX, int newY); virtual void VFoo(); virtual void VBar(int newX, int newY) = 0; static void SFoo(); static void SBar(int newX, int newY); static int sx; static int sy; };
對于上面列出的這個類MyClass,C++編譯器多數會以如下的方式進行編譯:
現在我們再來看一下為什么編譯器需要頭文件和符號地址就可以編譯鏈接一個使用MyClass的程序了。
首先,由于編譯器需要在編譯期就知道類的內存布局,以保證可以生成正確的開辟內存的代碼,及那些sizeof(MyClass)的值。有了頭文件,編譯器就知道,一個MyClass占用12字節的內存空間(見上圖,兩個整數和一個指針)。
其次,在調用MyClass的成員函數、靜態函數時,鏈接器需要知道這些函數的入口地址,如果無法提供入口地址,鏈接器就會報錯。
最后,在引用MyClass的靜態數據成員時,實際上與引用一個外部全局對象一樣,鏈接器需要知道這些變量的地址。如果無法提供這些變量的地址,鏈接器也會報錯。
可以看出:
1. 編譯期:必須要提供的是類的頭文件,以使編譯器可以得知類實例的尺寸和內存布局。
2. 鏈接期:必須要提供的是程序中引用過的,類的成員函數、靜態函數、靜態數據成員的地址,以使鏈接器可以正確的生成最終程序。
到這里,我們可以猜到,實際上,導出一個類,編譯器實際上只需要將這個類中的:成員函數、靜態函數、靜態數據成員當成普通的函數、全局變量導出即可。也就是說,我們實際上沒有“導出一個類”,而是把這個類中需要被引用的“有定義的實體”的入口地址像普通函數和變量那樣正常導出即可。
最后我們來看一下,實際上生成的一個導出前面列的的那個MyClass類的DLL。用Dependence來查看,可以看到下面的結果:
可以看到,除了VBar函數是一個純虛函數外,其它函數、靜態數據成員的入口地址都被導出。另外可以看到,vtable也被導出,以便操作虛函數時引用。
Balon白話MSDN:從普通DLL中導出C++類(1) – dllexport和dllimport的使用方法(中英對照、附注解)
我寫這些內容時偷了個懶,避開了虛表的一大堆復雜內容。謝謝houdy的提示,他的文章對于虛表,以及從DLL導出虛表的底層機制進行了詳細的剖析,想對此刨根問底的同學一定要看下:
虛函數表放在哪里?
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。