分離式編譯

2021-07-06 05:18:59 字數 1993 閱讀 6286

最近的工作中需要用到類模板。宣告模板類放在標頭檔案裡,然後犯了個錯誤把 類的成員函式定義按照慣例放在了乙個cpp檔案裡。編譯雖然過了,但是鏈結也會出問題。

這裡開始找找原因,原來類模板不支援分離編譯。

c++編譯過程分幾步。第一 預編譯 

這一步可以粗略的認為只做了一件事情,那就是「巨集展開」,也就是對那些#***的命令的一種展開,例如define max 1000就是建立起max和1000之間的對等關係,好在編譯階段進行替換。例如ifdef/ifndef就是從乙個檔案中有選擇性的挑出一些符合條件的**來交給下一步的編譯階段來處理。這裡面最複雜的莫過於include了,其實也很簡單,就是相當於把那個對應的檔案裡面的內容一下子替換到這條include***語句的地方來。

這也是為什麼 需要在標頭檔案裡 有預編譯頭 來處理 避免重新展開了該標頭檔案 保證每個標頭檔案最多被展開一次

#ifndef _win32_winnt            // specifies that the minimum required platform is windows vista.

#endif

編譯:c++標準中提到,乙個編譯單元[translation unit]是指乙個.cpp檔案以及它所include的所有.h檔案,.h檔案裡的**將會被擴充套件到包含它的.cpp檔案裡,然後編譯器編譯該.cpp檔案為乙個.obj檔案,後者擁有pe[portable executable,即windows可執行檔案]檔案格式,並且本身包含的就已經是二進位製碼,但是,不一定能夠執行,因為並不保證其中一定有main函式。

由於在類模板中編譯過程中 還沒有實現「具體實現化」的乙個過程。模板函式的**其實並不能直接編譯成二進位制**。所有在分離編譯的cpp檔案 編譯器不會去理會的。

關於模板類分離編譯 鏈結失敗的原因分析 找到了比較好的答案:

關鍵是:在分離式編譯的環境下,編譯器編譯某乙個.cpp檔案時並不知道另乙個.cpp檔案的存在,也不會去查詢[當遇到未決符號時它會寄希望

於聯結器]。這種模式在沒有模板的情況下執行良好,但遇到模板時就傻眼了,因為模板僅在需要的時候才會具現化出來,所以,當編譯器只看

到模板的宣告時,它不能具現化該模板,只能建立乙個具有外部連線的符號並期待聯結器能夠將符號的位址決議出來。然而當實現該模板

的.cpp檔案中沒有用到模板的具現體時,編譯器懶得去具現,所以,整個工程的.obj中就找不到一行模板具現體的二進位制**,於是聯結器也傻眼。

經過消化 也就是說上面一段內容指的是,在其他cpp檔案還沒有利用到模板類的時候,編譯器編譯其它cpp檔案,不需要去找模板類具體化的二進位制**,所以鏈結不會錯誤,但是如果其他cpp檔案裡用到模板類,那麼它就寄希望於鏈結到二進位制**。可以分離編譯模式下,正在編譯的某cpp不知道 類模板定義的cpp檔案的存在,它只知道

類模板的h檔案。如果模板在h檔案裡 定義好了 那麼就可以被編譯器具體化 生成需要的二進位制**鏈結進去。

換句話說 模板類 不能同其他類一樣生成obj,它只能附屬到呼叫它的cpp編譯單元去具體化產生二進位制**。我想這個二進位制**屬於具體化它的cpp的obj裡面的。

歡迎指正!

類模版定義和inline函式定義在.h裡,非inline函式和靜態放在.cpp裡。這種方式可以減少編譯相同的函式模版定義會增加的不必要的編譯時間。比如,類模板的定義在a.h裡,該類的成員函式和靜態成員在a.cpp裡,而主函式等一些使用者程式的使用在main.cpp裡。這裡,涉及到「可匯出的類模板」。

可匯出的類模板:當他的成員函式例項或者靜態例項被使用時,編譯器只要求類模板的定義。即使乙個類模板自身被宣告為可匯出的,export也只是單純的使成員函式和靜態可以在程式文字中不可見,而其自身的定義也仍然是必須得。

but,因為編譯的時候每次只執行乙個檔案,如果乙個成員函式多次被定義,編譯器也不能出錯,但是,鏈結時刻就有錯誤了。或者就是出錯,或者就是由編譯器可能用乙個可匯出的模板定義例項化該成員忽略其他的定義。

當然也有好處了。把介面和具體實現分離開來:介面在.h,實現在.c。不需要使用者知道具體細節,只用他們需要的函式就可以了。

p.s.使用包含編譯模式的時候,成員的定義被包含在我們所說的主函式所在的.cpp裡面。

C 分離式編譯

c 開發中廣泛使用宣告和實現分開的開發形式,其編譯過程是分離式編譯,就是說各個cpp檔案完全分開編譯,然後生成各自的obj目標檔案,最後通過鏈結器link生成乙個可執行的exe檔案。不需其他操作。在編譯main.cpp的時候,不需要知道呼叫的其他檔案中的函式的具體實現,只需要有個宣告,然後會有一條c...

C 中的分離式編譯

隨著程式越來越複雜,我們希望把程式的各個部分分別儲存在不同的檔案中。我們可以將原來的程式分成三個部分 pragma once ifndef game h define game h include using namespace std struct game void inputgame game...

匯流排的分離式通訊

同步通訊 非同步通訊和半同步通訊都是從主模組發出位址和讀寫命令開始,直到資料傳輸結束。在整個傳輸週期中,系統匯流排的使用權完全有占有使用權的主模組和由他選中的從模組佔據。進一步分析讀命令傳輸週期,發現除了申請匯流排這一階段外,其餘時間主要花費在如下3個方面。1.主模組通過傳輸匯流排向從模組傳送位址和...