1. 單獨編譯
和C語言一樣,C++也允許甚至鼓勵程序員將組件函數放在獨立的文件中。
可以單獨編譯這些文件,然后將它們鏈接成可執行的程序。
通常C++編譯器既編譯程序,也管理鏈接器。
如果只修改了一個文件,則可以只重新編譯該文件,然后將它與其他文件的編譯版本鏈接。
這使得大程序的管理更便捷。
頭文件中常包含以下內容:
函數原型,使用#define
或const
定義的符號常量,結構聲明,類聲明,模板聲明,內聯函數。
注意在包含頭文件時,我們使用coordin.h
而不是<coordin.h>
,
如果文件名包含在尖括號中,則C++編譯器將在存儲標準頭文件的主機系統的文件系統中查找,
但如果文件名包含在雙引號中,則C++編譯器首先查找當前工作目錄或源代碼目錄(或其他目錄,這取決于編譯器),
如果沒有在那里找到頭文件,則將在標準位置查找。
因此,在包含自己的頭文件時,應使用引號而不是尖括號。
2. 存儲持續性
C++使用三種(在C++11中是四種)不同的方案來存儲數據,這些方案的區別就在于數據保留在內存中的時間。
(1)自動存儲持續性
在函數定義中聲明的變量(包括函數參數)的存儲持續性為自動的,
它們在程序開始執行其所屬的函數或代碼塊時被創建,在執行完函數或代碼塊時,它們使用的內存被釋放。
(2)靜態存儲持續性
在函數外定義的變量,和使用關鍵字static
定義的變量的存儲持續性都為靜態。
它們在程序整個運行過程中都存在。
(3)線程存儲持續性(C++11)
當前多核處理器很常見,這些CPU可同時處理多個執行任務。
這讓程序能夠將計算放在可并行處理的不同線程中。
如果變量是使用關鍵字thread_local
聲明的,則其生命周期與所屬的線程一樣長。
(4)動態存儲持續性
用new
運算符分配的內存將一直存在,直到使用delete
運算符將其釋放或程序結束為止。
這種內存的存儲持續性為動態,有時被稱為自由存儲(free store)或堆(heap)。
注:
在C++11中,關鍵字auto
用于自動類型推斷,
但在C語言和以前的C++版本中,auto
的含義截然不同,它用于顯式的指出變量為自動存儲。
3. 作用域
作用域(scope)描述了名稱在文件(編譯單元)的多大范圍內可見。
例如,函數中定義的變量可在該函數中使用,但不能在其他函數中使用。
而在文件中的函數定義之前定義的變量則可在所有函數中使用。
C++變量的作用域有多種,作用域為局部的變量只在定義它的代碼塊中可用。
代碼塊是由花括號括起的一系列語句。
作用域為全局(也叫文件作用域)的變量,在定義位置到文件結尾之間都可用。
自動變量的作用域為局部,
靜態變量的作用域是全局還是局部取決于它是如何被定義的。
在函數原型作用域中使用的名稱只能在包含參數列表的括號內可用(這就是為什么這些名稱是什么以及是否出現都不重要的原因)。
在類中聲明的成員的作用域為整個類,
在名稱空間中聲明的變量的作用域為整個名稱空間(由于名稱空間已經引入到C++語言中,因此全局作用域是名稱空間作用域的特例)。
4. 變量的鏈接性
鏈接性(linkage)描述了名稱如何在不同單元間共享。
鏈接性為外部的名稱,可在文件間共享,鏈接性為內部的名稱,只能由一個文件中的函數共享。
自動變量的名稱沒有鏈接性,因為它不能共享。
鏈接性為外部的變量,通常簡稱為外部變量,它們的存儲持續性為靜態,作用域為整個文件。
4.1 extern
一方面,在每個使用外部變量的文件中,都必須聲明它,
另一方面,變量只能有一次定義。
C++提供了兩種操作,一種稱為定義,它給變量分配存儲空間。
另一種是引用聲明,它不給變量分配存儲空間,而是引用已有的變量。
引用聲明使用關鍵字extern
,且不進行初始化,否則將變成定義,導致分配空間。
double up; // definition, up is 0
extern int blem; // blem defined elsewhere
extern char gr = 'z'; // definition because initialized
extern char gr = 'z';
中的extern
是可以省略的。
如果在多個文件中使用外部變量,只需要在一個文件中包含該變量的定義,
但在使用該變量的其他所有文件中,都必須使用關鍵字extern
聲明它。
注:
省略extern
將使變量具有外部鏈接屬性。
4.2 static
(1)用于作用域為整個文件的變量時
將static
限定符用于作用域為整個文件的變量時,該變量的鏈接性將為內部的。
鏈接性為內部的變量只能在其所屬的文件中使用。
如果文件定義了一個靜態變量,其名稱與另一個文件中聲明的常規外部變量相同,
則在該文件中,靜態變量將隱藏常規外部變量。
// file1
int errors = 20; // external declaration
- - -
// file2
static int errors = 5; // known to file2 only
void froobish(){
cout << errors; // uses errors defined in file2
}
可使用外部變量在多文件程序的不同部分之間共享數據,
可使用鏈接性為內部的靜態變量在同一個文件中的多個函數之間共享數據。
(名稱空間提供了另外一種共享數據的方法)
如果將作用域為整個文件的變量變為靜態的,就不必擔心其名稱與其他文件中的作用域為整個文件的變量發生沖突。
(2)用于作用域為整個文件的變量時
將static
限定符用于在代碼塊中定義的變量時,將導致局部變量的存儲持續性為靜態的。
這意味著雖然該變量只在該代碼塊中可用,但它在該代碼塊不處于活動狀態時仍然存在。
因此,在兩次函數調用之間,靜態局部變量的值將保持不變。
另外,如果初始化了靜態局部變量,則程序只在啟動時進行一次初始化。
以后再調用函數時,將不會像自動變量那樣再次被初始化。
注:
關鍵字static
被用在作用域為整個文件的聲明中時,表示內部鏈接性,
被用于局部聲明中,表示局部變量的存儲持續性為靜態的。
關鍵字extern
表示引用聲明,即聲明引用在其他地方定義的變量。
關鍵字thread_local
指出變量的持續性與其所屬線程的持續性相同。
thread_local
變量之于線程,猶如常規靜態變量之于整個程序。
5. 函數的鏈接性
在默認情況下,函數的鏈接性為外部的,即可以在文件間共享。
可以在函數原型中使用關鍵字extern
來指出函數是在另一個文件中定義的,不過這是可選的。
可以使用關鍵字static
將函數的鏈接性設置為內部的,使之只能在一個文件中使用。
必須同時在原型和函數定義中使用static
關鍵字。
static int fn(double x);
...
static int fn(double x){
...
}
這意味著該函數只在這個文件中可見,還意味著可以在其他文件中定義同名的函數。
和變量一樣,在定義靜態函數的文件中,靜態函數將覆蓋外部定義,
因此,即使在外部定義了同名的函數,該文件扔將使用靜態函數。
單定義規則也適用于非內聯函數,因此對于每個非內聯函數,程序只能包含一個定義。
對于鏈接性味外部的函數來說,這意味著在多文件程序中,只能有一個文件包含該函數的定義,
但使用該函數的每個文件都應包含其函數原型。
內聯函數不受這種規則的約束,這允許程序員能夠將內聯函數的定義放在頭文件中,
這樣包含了頭文件的每個文件都有內聯函數的定義。
然而,C++要求同一個函數的素有內聯定義都必須相同。
注:
在默認情況下,函數的鏈接性為外部的,即可以在文件間共享。
6. 小結
所有聲明,都具有外部連接性。
6.1 具有內部連接性的定義
(1)名字空間(包括全局名字空間)中的靜態自由函數,靜態友元函數、靜態變量定義,const常量定義
(2)enum定義,類的定義,union的定義
(3)inline函數定義(包括自由函數和非自由函數)
6.2 具有外部連接性的定義
(1)非inline的類成員函數,非inline的類靜態函數
(2)類靜態成員變量
(3)名字空間(包括全局名字空間)中非靜態自由函數,非靜態友元函數,非靜態變量
注:
把一個帶有外部連接的定義放在 .h 文件中都會引起錯誤,
由于類的聲明和定義都是內部連接的,一般都放在 .h 文件中。