C++中頭文件(.h)和源文件(.cpp)都應該寫些什麼

原文鏈接:https://www.cnblogs.com/fenghuan/p/4794514.html

轉自:https://www.cnblogs.com/fenghuan/p/4794514.html

1..h叫做頭文件,它是不能被編譯的。“#include”叫做編譯預處理指令,可以簡單理解成,在1.cpp中的#include"1.h"指令把1.h中的代碼在編譯前添加到了1.cpp的頭部。每個.cpp文件會被編譯,生成一個.obj文件,然後所有的.obj文件鏈接起來你的可執行程序就算生成了。

發現了沒有,你要在.h文件中嚴格區分聲明語句和定義語句。好的習慣是,頭文件中應只處理常量、變量、函數以及類等等等等的聲明,變量的定義和函數的實現等等等等都應該在源文件.cpp中進行。

至於.h和.cpp具有同樣的主文件名的情況呢,對編譯器來講是沒有什麼意義的,編譯器不會去匹配二者的主文件名,相反它很傻,只認#include等語句。但是這樣寫是一種約定俗成的編程風格,一個類的名字作爲其頭文件和源文件的主文件名比如Class1.h和Class1.cpp,這個類的聲明在Class1.h中,實現在Class1.cpp中,我們人類看起來比較整齊,讀起來方便,也很有利於模塊化和源代碼的重用。

爲什麼這個風格會約定俗成?有一句著名的話,叫“程序是爲程序員寫的”。

2.h文件和cpp文件也就是說,在h文件中聲明Declare,而在cpp文件中定義Define。 “聲明”向計算機介紹名字,它說,“這個名字是什麼意思”。而“定義”爲這個名字分配存儲空間。無論涉及到變量時還是函數時含義都一樣。無論在哪種情況下,編譯器都在“定義”處分配存儲空間。對於變量,編譯器確定這個變量佔多少存儲單元,並在內存中產生存放它們的空間。對於函數,編譯器產生代碼,併爲之分配存儲空間。函數的存儲空間中有一個由使用不帶參數表或帶地址操作符的函數名產生的指針。定義也可以是聲明。如果該編譯器還沒有看到過名字A,程序員定義int A,則編譯器馬上爲這個名字分配存儲地址。聲明常常使用於extern關鍵字。如果我們只是聲明變量而不是定義它,則要求使用extern。對於函數聲明, extern是可選的,不帶函數體的函數名連同參數表或返回值,自動地作爲一個聲明。

另篇:

在C++編程過程中,隨着項目的越來越大,代碼也會越來越多,並且難以管理和分析。於是,在C++中就要分出了頭(.h)文件和實現(.cpp)文件,並且也有了Package的概念。

頭文件

頭文件的所有內容,都必須包含在

#ifndef {Filename} 
#define {Filename} 

//{Content of head file} 

#endif

這樣才能保證頭文件被多個其他文件引用(include)時,內部的數據不會被多次定義而造成錯誤

inline限定符

在頭文件中,可以對函數用inline限定符來告知編譯器,這段函數非常的簡單,可以直接嵌入到調用定義之處。

當然inline的函數並不一定會被編譯器作爲inline來實現,如果函數過於複雜,編譯器也會拒絕inline。

因此簡單說來,代碼最好短到只有3-5行的才作爲inline。有循環,分支,遞歸的函數都不要用做inline。

對於在類定義內定義實現的函數,編譯器自動當做有inline請求(也是不一定inline的)。因此在下邊,我把帶有inline限定符的函數成員和寫在類定義體內的函數成員統稱爲“要inline的函數成員”

非模板類型

全局類型

就像前面籠統的話講的:申明寫在.h文件。

對於函數來講,沒有實現體的函數,就相當於是申明;而對於數據類型(包括基本類型和自定義類型)來說,其申明就需要用extern來修飾。

然後在.cpp文件裏定義、實現或初始化這些全局函數和全局變量。

不過導師一直反覆強調:不許使用全局函數和全局變量。用了之後造成的後果,目前就是交上去的作業項目會扣分。當然不能用自有不能用的理由以及解決方案,不過不在目前的討論範圍內。

 

自定義類型

對於自定義類型,包括類(class)和結構體(struct),它們的定義都是放在.h文件中。其成員的申明和定義就比較複雜了,不過看上邊的表格,還是比較清晰的。

函數成員

函數成員無論是否帶有static限定符,其申明都放在.h文件的類定義內部。

對於要inline的函數成員其定義放在.h文件;其他函數的實現都放在.cpp文件中。

數據成員

數據成員的申明與定義都是放在.h文件的類定義內部。對於數據類型,關鍵問題是其初始化要放在什麼地方進行。

對於只含有static限定符的數據成員,它的初始化要放在.cpp文件中。因爲它是所有類對象共有的,因此必須對它做合適的初始化。

對於只含有const限定符的數據成員,它的初始化只能在構造函數的初始化列表中完成。因爲它是一經初始化就不能重新賦值,因此它也必須進行合適的初始化。

對於既含有static限定符,又含有const限定符的數據成員,它的初始化和定義同時進行。它也是必須進行合適的初始化

對於既沒有static限定符,又沒有const限定符的數據成員,它的值只針對本對象可以隨意修改,因此我們並不在意它的初始化什麼時候進行。

 

模板類型

C++中,模板是一把開發利器,它與C#,Java的泛型很相似,卻又不盡相同。以前,我一直只覺得像泛型,模板這種東西我可能一輩子也不可能需要使用到。但是在導師的強制逼迫使用下,我才真正體會到模板的強大,也真正知道要如何去使用模板,更進一步是如何去設計模板。不過這不是三言兩語可以講完的,就不多說了。

對於模板,最重要的一點,就是在定義它的時候,編譯器並不會對它進行編譯,因爲它沒有一個實體可用。

只有模板被具體化(specialization)之後(用在特定的類型上),編譯器纔會根據具體的類型對模板進行編譯。

所以才定義模板的時候,會發現編譯器基本不會報錯(我當時還很開心的:我寫代碼盡然會沒有錯誤,一氣呵成),也做不出智能提示。但是當它被具體用在一個類上之後,錯誤就會大片大片的出現,卻往往無法準確定位。

因此設計模板就有設計模板的一套思路和方式,但是這跟本文的主題也有偏。

 

因爲模板的這種特殊性,它並沒有自己的準確定義,因此我們不能把它放在.cpp文件中,而要把他們全部放在.h文件中進行書寫。這也是爲了在模板具體化的時候,能夠讓編譯器可以找到模板的所有定義在哪裏,以便真正的定義方法。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章