從普通DLL中匯出C++類 <二>

來源:互聯網
上載者:User

標籤:dllimport   dllexport   

    上一篇文章中,我們介紹了怎麼從一個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匯出虛表的底層機制進行了詳細的剖析,想對此刨根問底的同學一定要看下:
虛函數表放在哪裡?


本文出自 “GDT解說” 部落格,轉載請與作者聯絡!

從普通DLL中匯出C++類 <二>

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.