1樓:Coder
為什麼不推薦在標頭檔案中定義函式或者變數呢,只建議在標頭檔案中宣告變數和函式...
要記住,每當你用乙個#include的時候,你就是把那個標頭檔案的內容搬到#include的位置
#pragma once的作用是:在同乙個檔案中多次include同乙個標頭檔案的時候, 保證該標頭檔案內容只被替換一次, 並會為以下情況提供保證:多個不同檔案include同乙個標頭檔案, 然後這個標頭檔案內容只在其中的乙個檔案中被替換。
所以呢,如果你在兩個.c檔案(比如a.c, b.
c)都#include ''header.h'',並且header.h中定義乙個函式func(注意不是宣告), 那經過預處理階段後, a.
c和b.c中都會有func的函式體(定義體),那鏈結階段肯定會出錯———重複定義乙個函式,,,
解決也有很多辦法,比如在標頭檔案中把函式func宣告為static,或者inline, 但最根本的辦法還是,,只在標頭檔案中宣告變數和函式,而不是定義它們...
變數,函式可以多次宣告,,,,不可以多次定義
2樓:小小大轟龍
只有在 header 裡宣告,在 cpp 裡定義才能在編譯的時候得到每個類和函式的完全相同的定義。
如果沒有 header,所有的宣告和定義都在 cpp 裡面,編譯的時候也必須 include 這個 cpp 檔案,會增加編譯的時間。
方便同事查介面和函式原型。
c++20 引入了 module 應該會方便很多。
3樓:風吹歪
#include命令是把檔案所有內容整個替換到命令的位置,所以如果你的標頭檔案包含了實現,並且在多個檔案種include這個標頭檔案,就會產生重定義錯誤
但是只要你明白自己在做什麼,在標頭檔案裡定義也不是不可以,有很多single header library就是這樣做的,這樣的好處就是你可以像指令碼語言那樣import模組了,突出乙個方便
4樓:徐博的大哥
在我看來,cpp並不需要嚴格地遵從在標頭檔案中宣告,原始檔中定義這一最標準的準則!完全可以在標頭檔案中進行申明和定義(不僅僅是模版類,內聯函式,另外資料結構,類中類等等它們的宣告和定義都可以在標頭檔案中進行;另外,也可以完全在原始檔中進行申明定義,然後別的模組去include這個原始檔!唯一需要保證的是,在乙個工程的link階段,函式或者變數只有一次定義!
另外寫cpp的時候,標頭檔案也不必要用.h來作為字尾,原始檔也不必非要用cpp為字尾!cpp和h檔案的分離最大的目的,在我看來是為了便於組織管理,以及方便二次開發時的呼叫!
5樓:
首先,我來回答末尾的「 .cpp檔案和.h檔案相互之間的關係」。
講到.h就必然會提到#include。#include是什麼?
一條預處理指令。於是你就需要搞清楚預處理在c++程式的編譯過程中大約發生在什麼環節。以下引用 cppreference.
com:
The preprocessor is executed at translation phase 4, before the compilation. The result of preprocessing is a single file which is then passed to the actual compiler.
c++的預處理器在編譯之前執行,它看到#include指令,就會把那個檔案的內容替換到當前位置。其它的預處理指令例如#define, #ifdef, ##等也在這個階段被執行、並產生相應的內容。
預處理器執行完成後,所有的預處理指令都會被移除。其結果是乙個單個頭的大檔案(我猜測這檔案只存在於記憶體裡),這個檔案才會被進一步傳給編譯器做編譯。
搞清楚這一點很重要。例如為什麼有人說,標頭檔案多了拖慢編譯速度。因為乙個.
h被多個cpp include,每個cpp編譯的時候都要重新編譯一遍這個.h啊。所以VS允許預編譯標頭檔案,也有些別的編譯器支援類似功能。
其次,為什麼要把函式、全域性變數的宣告放到標頭檔案裡,把定義放在cpp裡呢?
因為c++發明那會兒考慮到與c的相容性,c的一些規則也被帶了過來,比如先宣告後使用。因此,如果你定義了乙個函式:
return_type
function
(parameter_type
param1
,...)
你要在別的cpp裡使用,你必須先宣告一下:
return_type
function
(parameter_type
param1
,...);
這主要是告訴編譯器:我知道這麼個函式,它簽名長這樣,它的定義在別的地方。現在我可以用它了吧?
然後問題來了:假如你這個函式到處都要用到,你總不能每個cpp裡貼上這麼一行吧。
答案自然就是把它放在標頭檔案裡咯。這就是你書裡那句話的意思。
至於cpp自己發明的那套,class什麼的,又不完全遵守c的那一套了。比如class裡的function A呼叫B,並不要求B必須寫在A前面。以上。
6樓:好知
首先了解一下C++的編譯原理,有很多規則需要遵循,比如說兩個class相互依賴,需要怎麼做才能讓編譯器通過,於是乎出現了"class A;"這種語法。同樣標頭檔案中宣告也是一樣的道理,說白了就是要遵循規則。有很多人可能說,不一定,我經常在標頭檔案裡面寫類函式的定義或者inline函式也沒問題啊。
從語法角度來說,不可否認只要編譯過怎麼做都可以。同樣,我們可以對比兩種做法的優缺點:
(1)原始碼修改
定義在標頭檔案, 所有包含該標頭檔案的cpp都需要重新編譯定義在原始檔,只有被修改的原始檔被編譯
所有在vc的stdafx檔案或者一些注釋當中會說明, 盡可能避免標頭檔案改動。
(2)原始碼閱讀
定義在標頭檔案,穿插函式的定義的函式宣告列表定義在原始檔,清晰的函式宣告列表
對比一下就知道那種更容易閱讀了。
暫且到此結束,有空繼續噴!
7樓:暗黑謝廣坤
那我反過來說吧,不在標頭檔案中宣告或不在原始檔中定義。
1、不在標頭檔案中宣告,使用函式需要自己宣告,否則編譯錯誤找不到宣告。
2、不在標頭檔案中宣告且在原始檔中宣告,同1。
3、不在原始檔中定義,鏈結錯誤提示找不到符號。
4、不在原始檔中定義且在標頭檔案定義,非static的情況下,多處include出現鏈結錯誤提示重複定義符號。static的情況下,多次重複定義導致最終執行檔案變大。
根源在於include等同於檔案內容歸併,鏈結查詢符號。
8樓:Xi Yang
因為C沒有模組的概念,編譯結果裡面沒有程式設計介面。如果你想讓你的內容模組化,就只能把程式設計介面寫在乙個檔案裡,並稱之為標頭檔案。
9樓:SuperFashi
怎麼寫都可以,你可以寫個hpp,把宣告都寫在裡面,cpp裡再寫實現,也完全可以不要hpp,全都寫在cpp裡面。
前者的好處是方便檢視宣告,你可以把注釋都寫在hpp裡面,告訴使用者怎麼呼叫函式。後者的好處是……寫的東西少一點。
我個人還是喜歡後者。
10樓:耶倫奶奶
要從幾個部分解釋一下。
第一,預編譯指令#include的作用是將所包含的檔案全文複製到#include的位置,相當於是個展開為乙個檔案的巨集。
第二,C++允許多次宣告,但只允許一次實現。比如int foo();就是一次宣告,而int foo(){}就是一次實現。
如果編譯時有多個.cpp檔案中#include了同乙個含有函式實現的.h,這時候鏈結器就會在多個目標檔案中找到這個函式的實現,而這在C++中是不允許的,此時就會引爆LNK1169錯誤:
找到乙個或多個重定義的符號。
因此為了讓函式可以在各個.cpp中共享,正確的做法就是在.h中只宣告函式,並在另【 乙個(重點)】.cpp中實現這個函式。這樣就不會衝突了。
11樓:林飛
*h可以視作戶口登記表有人名(函式名)長相(返回值,參數列),類宣告可以視作乙個團隊,變數宣告可以視作物品列表
cpp視作登記表對應的人,團隊,物品
h裡面寫了乙個可以辦事的人,團隊,物品在哪叫啥長什麼樣,可以分發給不同的人這些人根據登記表裡面的記錄,知道了自己是什麼樣的(能幹什麼,需要什麼才能幹,也就是函式宣告),以及幹這個的能力(函式實現)。根據自己需要找到對應的人,團隊,物品。
你不能在乙個程式裡擁有多個完全一樣的人,團隊,物品。但是登記表可以有很多份
編譯器會將cpp檔案編譯成二進位制檔案,而cpp的include會將h裡的內容直接接在cpp內容的前面,h一般會存在於多個cpp中,所以h通常是個登記表而不是實際的人
手機打明天再用電腦更
為什麼C 中,含有函式宣告的標頭檔案應該被包含在定義函式的原始檔中?
如果,你在second.cpp裡面的function 1 裡呼叫了function 2 就會編譯不通過。並且,會提示你找不到function 2的定義。要不你試一下呢? 大JoeJoe 在實際的專案程式設計中 cpp檔案中定義的函式分為兩種,一種是對外提供介面供外部呼叫的,一種是特定功能封裝成乙個函...
c 能否把所有要用的標頭檔案在某乙個標頭檔案中全inlcude了,然後其他檔案只用include這個
qugx0528 每個cpp都是乙個編譯單元,編譯時生成多個obj檔案,在link的時候,也會極大的拖慢速度。有這麼一種情況,我有乙個很大的UI介面,其對應的實現 操作類也很龐大,我想把他們分解到多個cpp實現中。這樣就產生了這麼乙個問題 代表ui的標頭檔案會在每個cpp中被include一次,這樣...
為什麼在C 中泛型型別不能是Explicit Layout的?
這個LayoutKind列舉的 Explicit 的值,文件是這樣定義的 在未管理記憶體中的每乙個物件成員的精確位置是被顯式控制的,服從於Pack欄位的設定。每個成員必須使用FieldOffsetAttribute指示該字段在型別中的位置。就是,成員必須有FieldOffsetAttribute去顯...