標籤:nbsp none 建議 一起 include 拷貝 預先處理指令 外部變數 做什麼
首先,我們可以將所有東西都放在一個.cpp檔案內,編譯器會將這個.cpp編譯成.obj,即編譯單元。一個程式可以由一個編譯單元組成,也可以由多個編譯單元組成。一個.cpp對應一個.obj,然後將所有的.obj連結起來(通過一個叫連結器的程式),組成一個.exe,即程式。如果一個.cpp要用到另一個.cpp定義的函數怎麼辦,只需在這個.cpp中寫上它的函式宣告。 連結器將所有的obj連結起來,但是如果碰巧有相同的函數或外部變數怎麼辦?C++可以通過一種叫做連結屬性的關鍵字來限定,某個函數是屬於整個程式公用的,還是只在一個編譯單元obj裡面使用,這些關鍵字就是extern(外部連結)和static(內部連結)。讓我們說說.h。其實沒有.h,程式也能很好的工作,但是當你發現一個外部連結的函數或外部變數,需要許多分聲明,因為只要使用到該函數的單元,就必須寫一份聲明在那個.cpp裡面,如果要修改會很麻煩!!!.h就是為瞭解決這個問題而誕生的,它包含了這些公用的東西,然後所有需要使用該函數的.cpp,只需要用#include包含進去便可,以後需要修改,只是修改一份內容。#include並不是什麼申請指令,只是將指定檔案的內容,原封不動的拷貝進來。
不是很嚴格的講,*.h檔案做的是類的聲明,包括類成員的定義和函數的聲明,而*.cpp檔案做的類成員函數的具體實現(定義)。一個*.h檔案和*.cpp檔案一般是配對的。在*.cpp檔案的第一行一般也是#include"*.h"檔案,其實也相當於把*.h檔案裡的東西複製到*.cpp檔案的開頭。所以,你全部寫在*.cpp檔案其實也是一樣的。
既然可以直接寫cpp,為什麼還要寫hpp?除了編程規範外,還涉及到類一個重要性質,就是封裝性。比如現在我們公司和另一家軟體公司合作,這樣就必然要互相提供一些軟體的資訊(比如一些類,它到底是要做什麼的),可是在提供這些資訊的同時我們又不像讓對方知道我們這些類的具體實現,畢竟這些是我們公司的演算法核心和心血啊。所以這個時候就可以把類的介面(這個類是要做什麼的)放在*.h檔案中,而具體類的實現放在 *.cpp檔案。這時候我們只要給對方公司*.h檔案就行了。這樣既提供了必要的資訊,又保護了我們的核心代碼。
1.最表面的機制是:標頭檔是程式的介面(是代碼介面),提供給程式員以 類、模版、函數等一系列的聲明,讓程式員知道應該怎麼調用裡面的“東西”。
2.從動態連結程式庫的角度看:標頭檔提供介面,使得程式員在需要載入一個庫函數的時候(這裡也僅僅是舉簡單的例子)查看標頭檔就知道怎麼載入這個動態庫內部的函數。
3.從軟體的擴充來說:將標頭檔作為介面,再去定義它的實現,這樣只要保證介面不變(標頭檔不變),就可以只修改實現檔案,而不必修改其他的實現代碼。比如你有一個sort()函數來排序,在一個大程式中,你後來發現這個sort()有更好的演算法,於是你只需要去修改函數的實現(修改.cpp檔案的sort()函數的代碼),其他使用這個函數的地方可以完全保持不變,這是分割技術的第一個好處
4.從編譯的角度看:
所有源檔案都是被編譯器分別劃分單元來分別編譯,在編譯的過程中,標頭檔被嵌入到實現檔案裡面一起作為一個編譯單元被編譯(實現檔案filename.cpp裡的#include "filename.h"這一行被替換成filename.h裡面的所有內容(實際上會把預先處理指令去掉,這才是預先處理最本質的作用))。
舉一個簡單的例子,你定義了sort()函數,在test.h標頭檔裡聲明,在test.cpp裡定義,這個時候在test.cpp裡面#include "test.h",並定義sort()函數。
你需要在標頭檔內部寫預先處理代碼
標頭檔的所有內容,都必須包含在
#ifndef {Filename}
#define {Filename}
//{Content of head file} 你的代碼寫在這裡
#endif
這樣才能保證標頭檔被多個其他檔案引用(include)時,內部的資料不會被多次定義而造成錯誤。
是blob.hpp裡的前兩行可以看到
最後一行:
c++中標頭檔(.h)和源檔案(.cpp)都應該寫些什嗎?
標頭檔(.h):
寫類的聲明(包括類裡面的成員和方法的聲明)、函數原型、#define常數等,但一般來說不寫出具體實現
源檔案(.cpp):
源檔案主要寫實現標頭檔中已經聲明的那些函數的具體代碼。需要注意的是,開頭必須#include一下實現的標頭檔,以及要用到的標頭檔。那麼當你需要用到自己寫的標頭檔中的類時,只需要#include進來就行了。
下面舉個最簡單的例子來描述一下,咱就求個圓面積。
第1步,建立一個空工程(以在VS2003環境下為例)。
第2步,在標頭檔的檔案夾裡建立一個名為Circle.h的標頭檔,它的內容如下:
#ifndef CIRCLE_H
#define CIRCLE_H
class Circle
{
private:
double r;//半徑
public:
Circle();//建構函式
Circle(double R);//建構函式
double Area();//求面積函數
};
#endif
注意到開頭結尾的先行編譯語句。在標頭檔裡,並不寫出函數的具體實現。
第3步,要給出Circle類的具體實現,因此,在源檔案夾裡建立一個Circle.cpp的檔案,它的內容如下:
#include "Circle.h"
Circle::Circle()
{
this->r=5.0;
}
Circle::Circle(double R)
{
this->r=R;
}
double Circle:: Area()
{
return 3.14*r*r;
}
需要注意的是:開頭處包含了Circle.h,事實上,只要此cpp檔案用到的檔案,都要包含進來!這個檔案的名字其實不一定要叫Circle.cpp,但非常建議cpp檔案與標頭檔相對應。
最後,我們建一個main.cpp來測試我們寫的Circle類,它的內容如下:
#include <iostream>
#include "Circle.h"
using namespace std;
int main()
{
Circle c(3);
cout<<"Area="<<c.Area()<<endl;
return 1;
}
注意到開頭時有#include "Circle.h"的聲明,證明我們使用到了我們剛才寫的Circle類。
至此,我們工程的結構為:
運行一下,輸出結果為:
注意這裡聲明與定義的區別:它們最本質的區別是定義只可以出現一次,聲明可以出現多次。聲明不分配空間,而定義是要分配空間的。
http://blog.csdn.net/praker/article/details/38231757
https://www.cnblogs.com/fenghuan/p/4794514.html
c++ cpp和hpp的關係